728x90
반응형
앞에서 만든 UploadFiles 컴포넌트와, Editor 컴포넌트를 사용하여, 작성 컴포넌트를 만들어보자.
NoticeWriteComponent.js
import React, { useState, useRef, useEffect } from 'react';
import ajax from '../../utils/ajax';
import Editor from '../EditorComponent';
import UploadFiles from '../UploadFiles';
import LFSelect from '../common/LFSelect';
import { Link } from "react-router-dom";
const NoticeWriteComponent = () => {
const tabs = [
{ value: '공지사항', text: '공지사항' },
{ value: '업데이트', text: '업데이트' }
]
const [id, setId] = useState(0);
const [title, setTitle] = useState('');
const [desc, setDesc] = useState('');
const [type, setType] = useState('공지사항');
const uploadReferenece = React.createRef();
async function onClickSearch() {
if (title.trim() == '') {
alert('제목을 입력해주세요'); return;
}
if (desc.trim() == '') {
alert('내용을 입력해주세요'); return;
}
await uploadReferenece.current.upload().then(function (result) {
const files = result;
ajax('/api/notice/saveNotice', { title: title, desc: desc, type: type, files: files }, (res) => {
if (res.data && res.data.ok === 1) {
alert('저장 완료');
setId(res.data.insertedId);
var linkToClick = document.getElementById('notice_Detail_Link');
linkToClick.click();
} else {
alert('공지사항을 저장하는 도중 오류가 발생하였습니다.')
}
}, null, true);
}).catch(function (err) {
});
}
function onEditorChange(value) {
setDesc(value)
}
return (
<div className="container" style={{ fontFamily: 'Noto Sans Korean,Malgun Gothic,sans-serif' }}>
<div className="lf-menu-nav"><span>설정</span><span>공지사항</span></div>
<div className="lf-contents pd12">
<div className="top-controls">
<a href="/"><button className="lf-button primary float-right">목록으로</button></a>
</div>
<div style={{ padding: "12px" }}>
<div className="form-group">
<LFSelect options={tabs} onChange={(event) => setType(event.target.value)}/>
</div>
<div className="form-group">
<input type="text" placeholder="제목" className="form-control" onChange={(event) => setTitle(event.target.value)} />
</div>
<UploadFiles ref={uploadReferenece} />
<Editor value={desc} onChange={onEditorChange} />
<div className="text-center pd12">
<button className="lf-button primary" onClick={onClickSearch}>저장</button>
</div>
<Link id="notice_Detail_Link" to={{ pathname: '/noticeDetail', state: { _id: id } }}></Link>
</div>
</div>
</div>
)
};
export default NoticeWriteComponent;
저장 후, 자동으로 넘어가는 상세조회 컴포넌트도 작성해보자
NoticeDetailComponent.js
문자열을 HTML태그로 렌더링하기 위해서는 'dangerouslySetInnerHTML'을 사용하면 된다.
본인이 작성한 글인 경우에, 상세화면 하단에 '수정'과 '삭제' 버튼이 보이도록 처리한다.
import React, { useState, useEffect } from 'react';
import { Link } from "react-router-dom";
import ajax from '../../utils/ajax'
import moment from 'moment';
import Editor from '../EditorComponent';
import 'react-quill/dist/quill.snow.css';
const NoticeDetailComponent = (props) => {
const { location: { state: { _id } } } = props;
const [post, setPost] = useState([]);
const [fileList, setFileList] = useState([]);
const [isWriter, setIsWriter] = useState(false);
useEffect(() => {
const fetchPost = async () => {
let res = await ajax('/api/notice/noticeDetail', { _id });
if (res.data && res.data.length == 0) {
alert('조회된 결과가 없습니다');
} else {
setPost(res.data.result);
setFileList(res.data.result.fileList);
setIsWriter(res.data.result.isWriter)
await ajax('/api/notice/saveNoticeView', { _id });
}
}
fetchPost();
}, []);
function onClickDeleteNotice() {
if (confirm('삭제 하시겠습니까?')) {
ajax('/api/notice/deleteNotice', { _id: _id }, (res) => {
if (res.data && res.data.ok === 1) {
alert('삭제 완료');
location.href = '/';
}
})
}
}
return (
<div className="container" style={{ overflow: 'auto' }}>
<div className="lf-menu-nav"><span>공지사항</span></div>
<div className="lf-contents pd12">
{/* align-right */}
<div className="top-controls">
<a href="/"><button className="lf-button primary float-right">목록으로</button></a>
</div>
<div style={{ padding: "12px" }}>
<table className="notice-table">
<colgroup>
<col width="10%" />
<col width="40%" />
<col width="10%" />
<col width="40%" />
</colgroup>
<thead>
<tr>
<th>구분</th>
<td colSpan="3">{post.type}</td>
</tr>
<tr>
<th>제목</th>
<td colSpan="3">{post.title}</td>
</tr>
<tr>
<th>작성자</th>
<td>{post.userName}</td>
<th>작성일시</th>
<td>{moment(post.date).format('YYYY-MM-DD')}</td>
</tr>
<tr>
<th>첨부파일</th>
<td colSpan="3">
{fileList.map((name, index) => (
<span key={index}> <a href={'/uploads/' + name} target="_blank"
>{name}
</a> |</span>
))}
</td>
</tr>
</thead>
<tbody>
<tr>
<td className="notice-contents" colSpan="4" dangerouslySetInnerHTML={{
__html: post.desc
}}></td>
</tr>
</tbody>
</table>
</div>
{
isWriter &&
<div className="text-center mb8">
<button className="lf-button dark-gray" onClick={onClickDeleteNotice}>삭제</button>
<Link to={{ pathname: '/noticeModify', state: { _id: post._id } }}><button className="lf-button primary ml8">수정</button></Link>
</div>
}
</div>
</div>
)
};
export default NoticeDetailComponent;
728x90
반응형
'React' 카테고리의 다른 글
REACT] React Router, React Media (0) | 2020.10.12 |
---|---|
React] 배열 렌더링 How To Render Arrays in React (0) | 2020.10.08 |
React 게시판 만들기 : 파일업로드 multer (5) (0) | 2020.10.05 |
React 게시판 만들기 : React-Quill (4) (0) | 2020.09.29 |
React 게시판 만들기 : Pagination (3) (0) | 2020.09.28 |
댓글