458 lines
22 KiB
JavaScript
458 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";
|
|
import Button from "react-bootstrap/Button";
|
|
import {InputGroup} from "react-bootstrap";
|
|
import ViewerTree from "./ViewerTree";
|
|
|
|
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 [docSummary, setDocSummary] = useState();
|
|
const [docDetail, setDocDetail] = useState();
|
|
const [errorSelector, setErrorSelector] = useState();
|
|
|
|
const [show, setShow] = useState(false);
|
|
const [bookMarkModal, setBookMarkModal] = useState();
|
|
|
|
const [colList, setColList] = useState([3,2,7]);
|
|
|
|
|
|
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)=>{
|
|
setDocLoading(true);
|
|
setDocCode(docCode);
|
|
getCodeInfo(docCode);
|
|
getCodeDetailInfo(docCode);
|
|
}
|
|
|
|
const getCodeInfo = useCallback((docCode) => {
|
|
console.groupCollapsed("EgovMain.getCodeInfo()");
|
|
EgovNet.requestFetch(
|
|
'/standardCode/code-info?docCode='+docCode,
|
|
{
|
|
method: "GET",
|
|
headers: {
|
|
'Content-type': 'application/json'
|
|
}
|
|
},
|
|
(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("저장에 실패하였습니다.")
|
|
}
|
|
}
|
|
)
|
|
}
|
|
function treeControl(){
|
|
if(colList[0]===3){
|
|
setColList([0,3,9]);
|
|
}else{
|
|
setColList([3,2,7]);
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
getCodeInfo(docCode);
|
|
getCodeDetailInfo(docCode);
|
|
}, []);
|
|
|
|
console.log("------------------------------viewer [End]");
|
|
console.groupEnd("viewer");
|
|
return (
|
|
<>
|
|
{/*{treeLoading || docLoading? ():()}*/}
|
|
<Loading loadingState={treeLoading || docLoading}/>
|
|
<Row className={`mx-0 ${treeLoading || docLoading?'d-none':''}`}>
|
|
<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="2단비교"/>
|
|
<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"}>
|
|
<InputGroup>
|
|
<Form.Control type="text" size={"sm"} placeholder={"문서 내 검색"}/>
|
|
{errorSelector}
|
|
</InputGroup>
|
|
</Col>
|
|
</Row>
|
|
</Col>
|
|
</Row>
|
|
</Col>
|
|
<Col xs={colList[0]} className={`border-end viewerDiv ${colList[0]===3?'':'d-none'}`}>
|
|
{/*{codeTree}*/}
|
|
<ViewerTree docCode={docCode} updateDocCode={updateDocCode} setTreeLoading={setTreeLoading}/>
|
|
</Col>
|
|
<Col xs={colList[1]} className="border-end viewerDiv">
|
|
<div>
|
|
<Button size={"sm"} variant={"outline-secondary"} onClick={treeControl}>{colList[0]===3?'트리 접기':'트리 펼치기'}</Button>
|
|
</div>
|
|
{docSummary}
|
|
</Col>
|
|
<Col xs={colList[2]} className="viewerDiv detailInfoDiv" ref={actionAppend}>
|
|
{docDetail}
|
|
</Col>
|
|
</Row>
|
|
<Modal show={show} onHide={handleClose} size="xl" keyboard={false} scrollable>
|
|
{bookMarkModal}
|
|
</Modal>
|
|
</>
|
|
);
|
|
}
|
|
|
|
CodeViewer.defaultProps = {
|
|
docCode: 'KDS 11 10 15',
|
|
docName: '지반계측'
|
|
}
|
|
|
|
export default CodeViewer;
|