vue: Board(WIP)

This commit is contained in:
2021-09-05 03:59:00 +09:00
parent 4efb380a61
commit 1c985e227f
12 changed files with 472 additions and 190 deletions
+200
View File
@@ -0,0 +1,200 @@
<template>
<top-back-bar :title="title" />
<table id="newArticle" class="bg0">
<thead>
<tr>
<td colspan="2" class="newArticleHeader bg2"> 게시물 작성</td>
</tr>
</thead>
<tbody>
<tr>
<th class="bg1" style="width: 66px">
<span class="articleTitle">제목</span>
</th>
<td>
<input
class="titleInput"
type="text"
maxlength="250"
placeholder="제목"
/>
</td>
</tr>
<tr>
<th class="bg1">내용</th>
<td class="boardArticle">
<textarea class="contentInput autosize" placeholder="내용"></textarea>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">
<button type="button" id="submitArticle">등록</button>
</td>
</tr>
</tfoot>
</table>
<div id="board">
<!--<suspense>
<template #default>-->
<board-article
v-for="article in articles"
:key="article.no"
:article="article"
/>
<!--</template>
<template #fallback>
<span>불러오는 중입니다...</span>
</template>
</suspense>-->
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, reactive, toRef } from "vue";
import "../scss/bootstrap5.scss";
import "../scss/inheritPoint.scss";
import "../scss/game_bg.scss";
import TopBackBar from "./components/TopBackBar.vue";
import BoardArticle from "./components/BoardArticle.vue";
import _, { isArray } from "lodash";
import { ref } from "vue";
import axios from "axios";
import { convertFormData } from "./util/convertFormData";
import { InvalidResponse } from "./defs";
import { delay } from "./util/delay";
declare const isSecretBoard: boolean;
export type BoardResponse = {
result: true;
articles: Record<number, BoardArticleItem>;
};
export type BoardArticleItem = {
no: number;
nation_no: number;
is_secret?: boolean;
date: string;
general_no: number;
author: string;
author_icon: string;
title: string;
text: string;
comment: BoardCommentItem[];
};
export type BoardCommentItem = {
no: number;
nation_no: number;
is_secret?: boolean;
date: string;
document_no: number;
general_no: number;
author: string;
text: string;
};
export default defineComponent({
name: "Board",
components: {
TopBackBar,
BoardArticle,
},
async setup() {
let boardResponse: BoardResponse;
try {
const response = await axios({
url: "j_board_get_articles.php",
responseType: "json",
method: "post",
data: convertFormData({
isSecret: isSecretBoard,
}),
});
const result: InvalidResponse | BoardResponse = response.data;
if (!result.result) {
throw result.reason;
}
boardResponse = result;
} catch (e) {
console.error(e);
alert(`에러: ${e}`);
return;
}
//const articles = reactive();
console.log(boardResponse);
const articles:BoardResponse['articles'] = isArray(boardResponse.articles)?{}:boardResponse;
return {
title: isSecretBoard ? "기밀실" : "회의실",
articles,
};
},
});
</script>
<style>
#newArticle {
width: 1000px;
margin: 0 auto;
}
.titleInput {
width: 100%;
color: white;
background-color: transparent;
border: none;
margin: 1px 5px;
}
.contentInput {
width: 100%;
min-height: 3em;
color: white;
background-color: transparent;
border: none;
padding: 1px 5px;
}
.articleFrame {
width: 1000px;
margin: 20px auto;
}
.commentText {
width: 100%;
}
.authorName,
.comment .author {
width: 110px;
font-size: 85%;
}
.date {
width: 125px;
font-size: 85%;
}
.text {
text-align: left;
padding: 1px 5px;
}
.submitComment {
width: 100%;
}
.commentText {
color: white;
background-color: transparent;
border: none;
padding: 1px 5px;
}
</style>
+72
View File
@@ -0,0 +1,72 @@
<template>
<table class="articleFrame bg0">
<thead>
<tr class="bg1">
<th class="authorName"></th>
<th class="articleTitle"></th>
<th class="date"></th>
</tr>
</thead>
<tbody>
<tr>
<td><img class="authorIcon generalIcon" width="64" height="64" /></td>
<td class="text" colspan="2"></td>
</tr>
</tbody>
<tbody class="commentList">
<board-comment
v-for="comment in article.comment"
:key="comment.no"
:comment="comment"
/>
</tbody>
<tfoot>
<tr>
<td class="bg2 inputCommentHeader">댓글 달기</td>
<td>
<input
class="commentText"
type="text"
maxlength="250"
placeholder="새 댓글 내용"
:value="newCommentText"
/>
</td>
<td><button type="button" class="submitComment">등록</button></td>
</tr>
</tfoot>
</table>
</template>
<script lang="ts">
import { BoardArticleItem } from "board.vue";
import BoardComment from "./BoardComment.vue";
import { defineComponent, PropType } from "vue";
export default defineComponent({
name: "BoardArticle",
components: {
BoardComment,
},
data() {
return {
newCommentText: "",
};
},
props: {
article: {
type: Object as PropType<BoardArticleItem>,
required: true,
},
},
methods: {
submitComment() {
console.log(this.newCommentText);
},
},
/*
setup(props) {
toRef(props, "article");
},
*/
});
</script>
+22
View File
@@ -0,0 +1,22 @@
<template>
<tr class="comment">
<th class="author">{{ comment.author }}</th>
<td class="text">{{ comment.text }}</td>
<td class="date">{{ comment.date }}</td>
<!-- 삭제도 나중에 추가? -->
</tr>
</template>
<script lang="ts">
import { BoardCommentItem } from "board.vue";
import { defineComponent, PropType } from "vue";
export default defineComponent({
name: "BoardComment",
props: {
comment: {
type: Object as PropType<BoardCommentItem>,
required: true,
},
},
});
</script>
+51
View File
@@ -0,0 +1,51 @@
<template>
<table style="width: 1000px; margin: auto" class="tb_layout bg0">
<tr>
<td style="text-align: left">
{{title}}<br /><button
type="button"
class="btn btn-primary"
@click="back"
>돌아가기</button><br />
</td>
</tr>
</table>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue";
import "../../scss/game_bg.scss";
export default defineComponent({
name: "TopBackBar",
methods: {
back(){
if(this.type === 'normal'){
location.href = './';
}
else if(this.type == 'chief'){
location.href = 'b_chiefcenter.php';
}
else{
//TODO: window.close하려면 부모창이 있어야함!
window.close();
}
}
},
props: {
title: {
type: String,
required: true,
},
type: {
type: String as PropType<"normal"|"chief"|"close">,
default: "normal",
required: false,
},
},
});
</script>
<style>
</style>
+6 -13
View File
@@ -2,12 +2,12 @@
import axios from 'axios';
import $ from 'jquery';
import { trim } from 'lodash';
import { escapeHtml } from "./legacy/escapeHtml";
import { nl2br } from "./util/nl2br";
import { InvalidResponse } from './defs';
import { convertFormData } from './util/convertFormData';
import { setAxiosXMLHttpRequest } from './util/setAxiosXMLHttpRequest';
import { unwrap_any } from './util/unwrap_any';
import { escapeHtml } from "./escapeHtml";
import { nl2br } from "../util/nl2br";
import { InvalidResponse } from '../defs';
import { convertFormData } from '../util/convertFormData';
import { setAxiosXMLHttpRequest } from '../util/setAxiosXMLHttpRequest';
import { unwrap_any } from '../util/unwrap_any';
declare const isSecretBoard: boolean;
@@ -212,13 +212,6 @@ async function loadArticles() {
alert(`에러: ${e}`);
return;
}
return $.post({
url: 'j_board_get_articles.php',
dataType: 'json',
data: {
isSecret: isSecretBoard, //첫 버전이니까 전체 다 불러오자
}
});
}
function resizeTextarea($obj: JQuery<HTMLElement>) {
+7
View File
@@ -0,0 +1,7 @@
export function delay(ms: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
}
+6
View File
@@ -0,0 +1,6 @@
import { createApp } from 'vue'
import Board from './Board.vue';
import { setAxiosXMLHttpRequest } from './util/setAxiosXMLHttpRequest';
setAxiosXMLHttpRequest();
createApp(Board).mount('#app')