kcscDev/egovframe-template-simple-r.../src/pages/standardCode/viewer.js

479 lines
22 KiB
JavaScript

import React, { useState, useEffect, useCallback } from 'react';
import { useLocation, useParams } from 'react-router-dom';
import SbItem from './SbItem'
import Loading from '../../components/Loading'
import BookmarkModal from './BookmarkModal';
import {SbContainer} from './Sb.style'
import {VwDiv, VwPtag} from './Vw.style'
import Form from 'react-bootstrap/Form'
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Modal from 'react-bootstrap/Modal';
import * as EgovNet from 'api/egovFetch';
import {getLocalItem} from "../../utils/storage";
import CODE from "../../constants/code";
import {parseJwt} from "../../utils/parseJwt";
function CodeViewer(props) {
const [treeLoading, setTreeLoading] = useState(true);
const [docLoading, setDocLoading] = useState(true);
const {linkedDocCode} = useParams();
const [docCode, setDocCode] = useState(linkedDocCode !== undefined?linkedDocCode.split(':')[0]:props.docCode);
const [ymd, setYmd] = useState(linkedDocCode !== undefined?linkedDocCode.split(':')[1]:props.ymd);
const [docInfo, setDocInfo] = useState();
const [codeTree, setCodeTree] = useState();
const [docSummary, setDocSummary] = useState();
const [docDetail, setDocDetail] = useState();
const [errorSelector, setErrorSelector] = useState();
const [show, setShow] = useState(false);
const [bookMarkModal, setBookMarkModal] = useState();
const sessionUser = parseJwt(getLocalItem('accessToken'));
const sessionUserSe = sessionUser?.userSe;
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
console.group("viewer");
console.log("[Start] viewer ------------------------------");
console.log("viewer [props] : ", props);
const location = useLocation();
console.log("viewer [location] : ", location);
console.log("viewer [docCode] : ", docCode);
const updateDocCode = (docCode)=>{
setDocSummary([<div></div>])
setDocDetail([<div>불러오는중</div>])
setDocInfo([])
setDocCode(docCode);
getCodeInfo(docCode);
getCodeDetailInfo(docCode);
}
const getCodeTree = ()=>{
EgovNet.requestFetch(
'/standardCode/getCodeTree.do',
{
method: "POST",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({})
},
(resp) => {
const menuData = resp.result.codeTree;
// 코드 목록 트리 구성
// https://garve32.tistory.com/52 참고
const nest = (menuData, parent_seq = null, link = 'parent_seq') =>
menuData.filter(item => item[link] === parent_seq)
.map(item => ({ ...item, childrens: nest(menuData, item.seq) }));
const tree = nest(menuData);
let treeTag = [];
if(tree.length>0){
treeTag.push(
<SbContainer>
{tree.map((subItem) =>
<SbItem item={subItem} openDocCode={docCode} updateDocCode={updateDocCode} />
)}
</SbContainer>
)
}else{
treeTag.push(<div>검색된 결과가 없습니다.</div>); // 코드 목록 초기값
}
setCodeTree(treeTag);
setTreeLoading(false);
},
function (resp) {
console.log("err response : ", resp);
}
);
}
const getCodeInfo = useCallback((docCode) => {
console.groupCollapsed("EgovMain.getCodeInfo()");
EgovNet.requestFetch(
'/standardCode/getCodeInfo.do',
{
method: "POST",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({
docCode: docCode
})
},
(resp) => {
const docInfo = resp.result.docInfo;
// 헤더 연도 선택지 구성
let headTag = [];
if(docInfo.length>0){
let optionTag = [];
let activeIndex = 0;
let docTitle = "";
if(ymd===undefined){
activeIndex = docInfo.length-1
docTitle = docInfo[docInfo.length-1].doc_nm
}else{
docInfo.forEach(function (item, index){
if(new Date(item.rvsn_ymd) <= new Date(ymd)){
activeIndex = index;
docTitle = item.doc_nm
}
})
}
docInfo.forEach(function (item, index){
let buttonClass = "btn btn-sm docInfoBtn "
let pClass = "yearInfo ";
if(item.doc_er === 'E'){
buttonClass += "btn-success "
}else{
buttonClass += "btn-primary "
}
if(index === activeIndex){
buttonClass += "docInfoActive"
pClass += "yearInfoActive"
}
optionTag.push(
<Col>
<input type="button"
className={buttonClass}
value={item.doc_er==='E'?'제':'개'}
onClick={docInfoSelectorChange}
data-ymd={item.rvsn_ymd}
data-doccode={item.kcsc_cd}
data-docinfoseq={item.doc_info_seq}/>
<br/>
<p className={pClass}>{item.doc_yr}</p>
</Col>)
})
headTag.push(<Row className="docInfoRow"><Col className="docInfoTitle">{docCode} {docTitle}</Col>{optionTag}</Row>)
}else{
headTag.push(<div>검색된 결과가 없습니다.</div>); // 코드 목록 초기값
}
setDocInfo(headTag);
}
)
})
const getCodeDetailInfo = useCallback((docCode, ymd) => {
setErrorSelector([]);
console.groupCollapsed("EgovMain.getCodeDetailInfo()");
EgovNet.requestFetch(
'/standardCode/getCodeDetailInfo.do',
{
method: "POST",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({
docCode: docCode,
ymd: ymd
})
},
(resp) => {
// 목차 구성
let summaryTag = [];
// 문서 전문 구성
let detailTag = [];
// 문서 오류 선택 구성
let errorTag = [];
let optionTag = [];
//const activeYmd = document.querySelector(".docInfoActive").dataset.ymd;
if(resp.result.document.length>0){
const docLinkReg = /([A-Z]{3,5}(\s[0-9]{2}){3,4})/g
const docPartReg = /\((?:표|그림|부록)?\s*([A-Z]\.)?(?!\d\))\d+(\.\d+)*(\s?\(\d\))?(-\d+)?(?:\s*[A-Z])?\)/g // /(\((?:표|그림|부록)?\s*([A-Z]\.)?\d+(\.\d+)*(\s?\(\d\))?(-\d+)?(?:\s*[A-Z])?\))/g
let parentContCd = "";
let lastLevel = 0;
resp.result.document.forEach(function (item, index){
const isTitle = item.full_content.includes(item.group_title);
if(isTitle){
summaryTag.push(
<VwDiv depth={item.cont_level} isTitle={isTitle}>
<VwPtag href="#" isTitle={isTitle} onClick={() => {
document.location.hash="#"+item.cont_type_cd;
}} dangerouslySetInnerHTML={ {__html: item.group_title} }>
</VwPtag>
</VwDiv>
)
if(lastLevel < item.cont_level){
parentContCd = parentContCd+" "+item.cont_type_cd;
}else{
const contCdAry = parentContCd.split(" ");
parentContCd = "";
contCdAry.forEach(function (contCd, index){
if(index+1<item.cont_level){
parentContCd = parentContCd+" "+contCd
}
})
parentContCd = parentContCd+" "+item.cont_type_cd;
}
lastLevel = item.cont_level;
parentContCd = parentContCd.trim();
item.full_content = "<input type='checkbox' " +
"class='titleCheckBox "+parentContCd+"' " +
"id='"+item.cont_type_cd+"chk' " +
">"+item.full_content;
if(index!==0 && item.cont_level===1 && item.cont_order !== 1){
item.full_content = "<br>"+item.full_content;
}
}
if(!item.full_content.includes("<table")){
if(docLinkReg.test(item.full_content)){
const docCodeAry = item.full_content.match(docLinkReg);
const docCodeMap = new Map();
for(let i=0; i<docCodeAry.length; i++) {
const docCode = docCodeAry[i];
//docCodeMap.set(docCode, '<a class="docLink" href="/standardCode/viewer/' + docCode + '" target="_blank">' + docCode + '</a>')
docCodeMap.set(docCode, '<a class="docLink" href="#" data-doccode="'+docCode+'">' + docCode + '</a>')
}
docCodeMap.forEach((docCodeLink, docCode)=>{
item.full_content = item.full_content.replaceAll(docCode, docCodeLink);
})
if(docPartReg.test(item.full_content)){
const docPartAry = item.full_content.match(docPartReg);
const docPartMap = new Map();
for(let i=0; i<docPartAry.length; i++) {
const docPart = docPartAry[i];
const docCodeMatch = item.full_content.substring(item.full_content.indexOf(docPart)-30).match(docLinkReg);
let docCode = null;
if (docCodeMatch){
docCode = docCodeMatch[0];
}
//const docCode = item.full_content.substring(item.full_content.indexOf(docPart)-30).match(docLinkReg)[0];
docPartMap.set(docPart, docPart +
'<key class="bookmark" data-doccode="'+docCode+'" data-docpart="'+docPart.replace('(', '').replace(')', '')+'">'+
'<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M2 4a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v11.5a.5.5 0 0 1-.777.416L7 13.101l-4.223 2.815A.5.5 0 0 1 2 15.5V4z"></path><path d="M4.268 1A2 2 0 0 1 6 0h6a2 2 0 0 1 2 2v11.5a.5.5 0 0 1-.777.416L13 13.768V2a1 1 0 0 0-1-1H4.268z"></path></svg>'+
'</key>');
}
docPartMap.forEach((docPartLink, docPart)=>{
item.full_content = item.full_content.replaceAll(docPart, docPartLink);
})
}
}
}
if(item.full_content.includes("<table")){
item.full_content = item.full_content.replace('<table ', '<table class="table table-bordered "')
}
if(item.error_cd !== null){
if(sessionUserSe === "ACC_TP01"){
item.full_content = "<p class='errorText' id='error"+item.doc_cont_seq+"'>"+item.error_cd+"</p><br>"+
"<div class='input-group w-25 d-none errorEditDiv'>" +
"<input type='hidden' class='docContSeq' value='"+item.doc_cont_seq+"' />"+
"<input type='text' class='form-control form-control-sm errorCd' value='"+item.error_cd+"'/>"+
"<button class='btn btn-sm btn-primary errorEditSaveBtn'>저장</button>"+
"<button class='btn btn-sm btn-secondary errorEditCancelBtn'>취소</button>"+
"</div>"+
item.full_content;
}else{
item.full_content = "<p class='errorText' id='error"+item.doc_cont_seq+"'>"+item.error_cd+"</p><br>"+item.full_content;
}
optionTag.push(<option value={item.doc_cont_seq}>{item.error_cd}</option>)
}
item.full_content = item.full_content+"<hr>"
detailTag.push(
<VwDiv depth={item.cont_level} isTitle={isTitle} id={item.cont_type_cd} dangerouslySetInnerHTML={ {__html: item.full_content} }></VwDiv>
)
})
}else{
summaryTag.push(<li key="0">검색된 결과가 없습니다.</li>);
}
setDocSummary(summaryTag);
setDocDetail(detailTag);
if(optionTag.length>0){
errorTag.push(<Form.Select size="sm" onChange={errorSelectorChange}><option value="">선택</option>{optionTag}</Form.Select>)
setErrorSelector(errorTag);
}
setDocLoading(false);
document.querySelector(".detailInfoDiv").scrollTop = 0
},
function (resp) {
console.log("err response : ", resp);
}
);
console.groupEnd("EgovMain.getCodeDetailInfo()");
},[]);
const docInfoSelectorChange = useCallback((el) => {
setDocSummary([<div></div>])
setDocDetail([<div>불러오는중</div>])
const activeBtn = document.querySelector(".docInfoActive")
activeBtn.className = activeBtn.className.replace('docInfoActive', '').trim();
const activeP = document.querySelector(".yearInfoActive")
activeP.className = activeP.className.replace('yearInfoActive', '').trim();
const clickBtn = el.target;
clickBtn.className += " docInfoActive";
clickBtn.parentElement.querySelector("p").className += " yearInfoActive"
getCodeDetailInfo(clickBtn.dataset.doccode, clickBtn.dataset.ymd);
})
const actionAppend = (el) => {
if(!el) return;
if(el.childNodes.length===0){
return
}else{
docLinkActionAppend(el)
bookmarkBtnActionAppend(el)
checkboxActionAppend(el)
if(sessionUserSe === "ACC_TP01"){
errorTextActionAppend(el)
errorEditSaveBtnActionAppend(el)
errorEditCancelBtnActionAppend(el)
}
}
}
const docLinkActionAppend = (el) => {
const docLinkList = el.getElementsByClassName("docLink")
for(let docLink of docLinkList){
docLink.onclick = (e) => {
const ymd = document.querySelector(".docInfoActive").dataset.ymd;
window.open("/standardCode/viewer/"+e.target.dataset.doccode+":"+ymd);
}
}
}
const bookmarkBtnActionAppend = (el) => {
const bookmarkList = el.getElementsByClassName("bookmark")
for(let bookmark of bookmarkList){
bookmark.onclick = (e) => {
handleShow();
const params = e.currentTarget.dataset
setBookMarkModal(<BookmarkModal docCode={params.doccode} docPart={params.docpart} ymd={document.querySelector(".docInfoActive").dataset.ymd}/>)
}
}
}
const checkboxActionAppend = (el) => {
const checkboxList = el.getElementsByClassName("titleCheckBox")
for(let checkbox of checkboxList){
checkbox.onclick = (e) => {
const flag = e.target.checked;
const childCheckboxList = document.querySelectorAll('.'+e.target.id.replace('chk', ''))
childCheckboxList.forEach((child)=> {
child.checked = flag
})
}
}
}
const errorTextActionAppend = (el) => {
const errorTextList = el.getElementsByClassName("errorText")
for(let errorText of errorTextList){
errorText.onclick = (e) => {
e.target.parentElement.querySelector(".errorText").className = "errorText d-none"
e.target.parentElement.querySelector(".errorEditDiv").className = "input-group w-25 errorEditDiv"
}
}
}
const errorEditSaveBtnActionAppend = (el) => {
const saveBtnList = el.getElementsByClassName("errorEditSaveBtn")
for(let saveBtn of saveBtnList){
saveBtn.onclick = (e) => {
const errorEditDiv = e.target.parentElement;
const content = {
docContSeq: errorEditDiv.querySelector(".docContSeq").value,
errorCd: errorEditDiv.querySelector(".errorCd").value
}
saveErrorCd(content, errorEditDiv)
hideErrorEditDiv(e)
}
}
}
const errorEditCancelBtnActionAppend = (el) => {
const cancelBtnList = el.getElementsByClassName("errorEditCancelBtn")
for(let cancelBtn of cancelBtnList){
cancelBtn.onclick = (e) => {
hideErrorEditDiv(e)
}
}
}
function hideErrorEditDiv(e){
const contentDiv = e.target.parentElement.parentElement;
contentDiv.querySelector(".errorText").className = "errorText"
contentDiv.querySelector(".errorEditDiv").className = "input-group w-25 errorEditDiv d-none"
contentDiv.querySelector(".errorCd").value = contentDiv.querySelector(".errorText").innerText;
}
function errorSelectorChange(e){
window.location.href = window.location.origin+window.location.pathname+"#error"+e.target.value
}
function saveErrorCd(content, errorEditDiv){
EgovNet.requestFetch(
'/standardCode/auth/saveErrorCd.do',
{
method: "POST",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(content)
},
(resp) => {
if (Number(resp.resultCode) === Number(CODE.RCV_SUCCESS)) {
alert("저장되었습니다.")
errorEditDiv.parentElement.querySelector(".errorText").innerText = content.errorCd
}else if(Number(resp.resultCode) === Number(CODE.RCV_ERROR_AUTH)){
console.log("토큰 갱신중.")
}else{
alert("저장에 실패하였습니다.")
}
}
)
}
useEffect(() => {
getCodeTree();
getCodeInfo(docCode);
getCodeDetailInfo(docCode);
}, []);
console.log("------------------------------viewer [End]");
console.groupEnd("viewer");
return (
<>
{treeLoading || docLoading? (<Loading/>):(
<Row className="mx-0">
<Col xs={12} className="border-bottom">
<Row>
<Col xs={3}></Col>
<Col xs={9}>
<Row className="justify-content-between">
<Col xs={"auto"}>
<Row>
<Col xs={"auto"}>{docInfo}</Col>
<Col>
<input type="button" className="btn btn-sm btn-primary optionBtn" value="연혁"/>
<input type="button" className="btn btn-sm btn-primary optionBtn" value="비교"/>
</Col>
</Row>
</Col>
<Col xs={"auto"}>{errorSelector}</Col>
</Row>
</Col>
</Row>
</Col>
<Col xs={3} className="border-end viewerDiv">
{codeTree}
</Col>
<Col xs={2} className="border-end viewerDiv">
{docSummary}
</Col>
<Col xs={7} className="viewerDiv detailInfoDiv" ref={actionAppend}>
{docDetail}
</Col>
</Row>
)}
<Modal show={show} onHide={handleClose} size="xl" keyboard={false} scrollable>
{bookMarkModal}
</Modal>
</>
);
}
CodeViewer.defaultProps = {
docCode: 'KCS 24 31 10',
docName: '용접(한계상태설계법)'
}
export default CodeViewer;