Compare commits

...

10 Commits

Author SHA1 Message Date
강석 최 fda47a31eb viewer.js 수정.
불필요한 useCallback() 삭제.
useEffect() 결합.
한 item에 여러 링크가 있는 경우 반영.
2023-10-20 11:57:30 +09:00
강석 최 a4eb7eb850 기준코드 링크 기능 추가. 2023-10-19 17:30:40 +09:00
강석 최 4e58a20acf 링크 기능 작업중. 2023-10-19 14:53:40 +09:00
강석 최 f73efb133c 열람문서 강조표시 2023-10-19 10:08:35 +09:00
강석 최 52170e85ee 기준코드 뷰어 작업중. 2023-10-18 18:10:44 +09:00
강석 최 bb200ffbca 기준코드 뷰어 목차, 내용 css 추가. 2023-10-18 16:19:00 +09:00
강석 최 85951254cd Merge branch 'master' of https://dev.azure.com/DBNTech/kcscDev/_git/kcscDev 2023-10-18 15:44:17 +09:00
강석 최 882e63d89d 기준코드뷰어에서 footer 삭제. 뷰어 div 높이 조정 2023-10-18 15:44:11 +09:00
강석 최 3e273376a7 Deleted .idea 2023-10-18 04:04:40 +00:00
강석 최 c35583e9fe 기준코드 뷰어 작업중. 2023-10-17 18:01:49 +09:00
12 changed files with 179 additions and 109 deletions

8
.idea/.gitignore vendored
View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/kcscDev.iml" filepath="$PROJECT_DIR$/.idea/kcscDev.iml" />
</modules>
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -213,6 +213,25 @@ public class StandardCodeController {
private final StandardCodeService standardCodeService; private final StandardCodeService standardCodeService;
@Operation(
summary = "건설기준코드 트리 조회",
description = "건설기준코드 트리 조회",
tags = {"StandardCodeController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@PostMapping(value = "/getCodeTree.do", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getCodeTree(@RequestBody StandardCodeVO param, @AuthenticationPrincipal LoginVO user)
throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("codeTree", standardCodeService.selectStandardCodeTree());
resultVO.setResult(resultMap);
return resultVO;
}
@Operation( @Operation(
summary = "건설기준코드 내용 조회", summary = "건설기준코드 내용 조회",
description = "건설기준코드 내용 조회", description = "건설기준코드 내용 조회",
@ -222,12 +241,11 @@ public class StandardCodeController {
@ApiResponse(responseCode = "200", description = "조회 성공"), @ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님") @ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
}) })
@PostMapping(value = "/viewer.do", consumes = MediaType.APPLICATION_JSON_VALUE) @PostMapping(value = "/getCodeDetailInfo.do", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO selectStandardCode(@RequestBody StandardCodeVO param, @AuthenticationPrincipal LoginVO user) public ResultVO getCodeDetailInfo(@RequestBody StandardCodeVO param, @AuthenticationPrincipal LoginVO user)
throws Exception { throws Exception {
ResultVO resultVO = new ResultVO(); ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>(); Map<String, Object> resultMap = new HashMap<>();
resultMap.put("codeTree", standardCodeService.selectStandardCodeTree());
resultMap.put("document", standardCodeService.selectStandardCodeDocument(param)); resultMap.put("document", standardCodeService.selectStandardCodeDocument(param));
resultVO.setResult(resultMap); resultVO.setResult(resultMap);
return resultVO; return resultVO;

View File

@ -2,35 +2,39 @@ import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
function EgovFooter() { function EgovFooter() {
return ( if(window.location.pathname==="/standardCode/viewer"){
<div className="footer"> return null;
<div className="inner"> }else{
<h1> return (
<Link to=""> <div className="footer">
<img className="w" src="/assets/images/logo_footer_w.png" alt="" /> <div className="inner">
<img className="m" src="/assets/images/logo_footer_m.png" alt="" /> <h1>
</Link> <Link to="">
</h1> <img className="w" src="/assets/images/logo_footer_w.png" alt="" />
<div className="info"> <img className="m" src="/assets/images/logo_footer_m.png" alt="" />
<p> </Link>
대표문의메일 : egovframeexample@gmail.com <span className="m_hide">|</span><br className="m_show" /> 대표전화 : 0000-0000 (000-0000-0000)<br /> </h1>
호환성확인 : 000-0000-0000 | 교육문의 : 0000-0000-0000 <div className="info">
</p> <p>
<p className="copy">Copyright © 2021 Ministry Of The Interior And Safety. All Rights Reserved.</p> 대표문의메일 : egovframeexample@gmail.com <span className="m_hide">|</span><br className="m_show" /> 대표전화 : 0000-0000 (000-0000-0000)<br />
</div> 호환성확인 : 000-0000-0000 | 교육문의 : 0000-0000-0000
<div className="right_col"> </p>
<Link to=""> <p className="copy">Copyright © 2021 Ministry Of The Interior And Safety. All Rights Reserved.</p>
<img className="w" src="/assets/images/banner_w_01.png" alt="" /> </div>
<img className="m" src="/assets/images/banner_m_01.png" alt="" /> <div className="right_col">
</Link> <Link to="">
<Link to=""> <img className="w" src="/assets/images/banner_w_01.png" alt="" />
<img className="w" src="/assets/images/banner_w_02.png" alt="" /> <img className="m" src="/assets/images/banner_m_01.png" alt="" />
<img className="m" src="/assets/images/banner_m_02.png" alt="" /> </Link>
</Link> <Link to="">
<img className="w" src="/assets/images/banner_w_02.png" alt="" />
<img className="m" src="/assets/images/banner_m_02.png" alt="" />
</Link>
</div>
</div> </div>
</div> </div>
</div> );
); }
} }
export default EgovFooter; export default EgovFooter;

View File

@ -77,7 +77,8 @@ const URL = {
//기준코드 //기준코드
STANDARD_CODE_LIST : "/standardCode/list", //건설기준코드/리스트 STANDARD_CODE_LIST : "/standardCode/list", //건설기준코드/리스트
STANDARD_CODE_VIEWER : "/standardCode/viewer/:docCode", //건설기준코드/뷰어 STANDARD_CODE_VIEWER : "/standardCode/viewer", //건설기준코드/뷰어
STANDARD_CODE_VIEWER_LINK : "/standardCode/viewer/:linkedDocCode", //건설기준코드/뷰어/새 창 링크
} }
export default URL; export default URL;

View File

@ -304,4 +304,9 @@
.calendar_info ul li {position: relative; padding: 15px 180px 15px 30px; border-bottom: 1px solid #c8d0d5; color: #222;} .calendar_info ul li {position: relative; padding: 15px 180px 15px 30px; border-bottom: 1px solid #c8d0d5; color: #222;}
.calendar_info ul li::before {content: ""; display: block; position: absolute; left: 20px; top: 27px; width: 2px; height: 2px; background: #222;} .calendar_info ul li::before {content: ""; display: block; position: absolute; left: 20px; top: 27px; width: 2px; height: 2px; background: #222;}
.calendar_info ul li a {color: #222; font-size: 16px; font-weight: 300;} .calendar_info ul li a {color: #222; font-size: 16px; font-weight: 300;}
.calendar_info ul li span {position: absolute; right: 15px; top: 15px; color: #808080; font-size: 16px; font-weight: 300;} .calendar_info ul li span {position: absolute; right: 15px; top: 15px; color: #808080; font-size: 16px; font-weight: 300;}
.viewerDiv{height: calc(100vh - 180px); overflow-y: auto;}
.openDoc{background-color: bisque;}
.docLink{background-color: aquamarine}
.docLink:hover{cursor: pointer}

View File

@ -29,6 +29,18 @@ export const SbTitle = styled.div`
} }
`; `;
export const VwDiv = styled.div`
display: flex;
align-items: center;
padding-left: ${props => (props.depth * 10)}px;
color: ${props => (props.isTitle?'darkcyan':'black')};
padding-top: ${props => (props.isTitle?'10':'')}px;
`;
export const VwPtag = styled.p`
cursor: ${props => (props.isTitle?'pointer':'')};
`;
// 제일 하위메뉴에서 클릭할 Link // 제일 하위메뉴에서 클릭할 Link
export const SbLink = styled(Link)` export const SbLink = styled(Link)`
color: inherit; color: inherit;

View File

@ -1,15 +1,26 @@
import {React, useState} from 'react' import {React, useState} from 'react'
import { useNavigate } from 'react-router-dom'; import {SbTitle, SbSub} from './Sb.style'
import {SbTitle, SbSub, SbLink} from './Sb.style'
import { FcFolder, FcOpenedFolder, FcFile } from 'react-icons/fc' import { FcFolder, FcOpenedFolder, FcFile } from 'react-icons/fc'
import { AiOutlinePlusSquare, AiOutlineMinusSquare } from 'react-icons/ai' import { AiOutlinePlusSquare, AiOutlineMinusSquare } from 'react-icons/ai'
const SbItem = ({ item }) => { const SbItem = ({item, openDocCode, updateDocCode}) => {
const [collapsed, setCollapsed] = useState(false); const find = (array, openDocCode) => {
const navigate = useNavigate(); var result;
array.some(o => result = o.doc_code === openDocCode ? o : find(o.childrens || [], openDocCode));
return result;
};
const collapsedFlag = find(item.childrens, openDocCode) !== undefined;
// openDocCode === item.doc_code+' '+item.doc_code_name
const [collapsed, setCollapsed] = useState(collapsedFlag);
function toggleCollapse() { function toggleCollapse() {
setCollapsed(prevValue => !prevValue); setCollapsed(prevValue => !prevValue);
} }
function changeOpenDoc(seq){
const prevOpenDoc = window.document.getElementsByClassName("openDoc")[0];
prevOpenDoc.className = prevOpenDoc.className.replace('openDoc', '');
const nextOpenDoc = window.document.getElementById("doc"+seq);
nextOpenDoc.className = nextOpenDoc.className+' openDoc';
}
if(item.childrens.length > 0){ if(item.childrens.length > 0){
const icon1 = collapsed?<AiOutlinePlusSquare />:<AiOutlineMinusSquare />; const icon1 = collapsed?<AiOutlinePlusSquare />:<AiOutlineMinusSquare />;
@ -19,7 +30,7 @@ const SbItem = ({ item }) => {
<SbTitle depth={item.doc_level} onClick={toggleCollapse}>{icon1}{icon2}&nbsp;{(item.doc_level === 1?'':item.doc_code)+' '+item.doc_code_name}</SbTitle> <SbTitle depth={item.doc_level} onClick={toggleCollapse}>{icon1}{icon2}&nbsp;{(item.doc_level === 1?'':item.doc_code)+' '+item.doc_code_name}</SbTitle>
<SbSub isOpen={collapsed}> <SbSub isOpen={collapsed}>
{item.childrens.map((child) => ( {item.childrens.map((child) => (
<SbItem item={child} /> <SbItem item={child} openDocCode={openDocCode} updateDocCode={updateDocCode} />
))} ))}
</SbSub> </SbSub>
</div> </div>
@ -27,15 +38,11 @@ const SbItem = ({ item }) => {
}else{ }else{
const icon = <FcFile />; const icon = <FcFile />;
return ( return (
<SbTitle depth={item.doc_level}> <SbTitle depth={item.doc_level} id={`doc${item.seq}`} className={item.doc_code === openDocCode?'openDoc':''}>
{/*<a onClick={()=>{ <a onClick={()=>{
navigate('/standardCode/viewer', { changeOpenDoc(item.seq)
state:{ updateDocCode(item.doc_code, item.doc_code_name)
docCode: item.doc_code }}>{icon}&nbsp;{(item.doc_level === 1?'':item.doc_code)+' '+item.doc_code_name}</a>
}
})
}}>{icon}&nbsp;{(item.doc_level === 1?'':item.doc_code)+' '+item.doc_code_name}</a>*/}
<SbLink to={"/standardCode/viewer/"+item.doc_code}>{icon}&nbsp;{(item.doc_level === 1?'':item.doc_code)+' '+item.doc_code_name}</SbLink>
</SbTitle> </SbTitle>
) )
} }

View File

@ -1,14 +1,15 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { useLocation, useParams } from 'react-router-dom'; import { useLocation, useParams } from 'react-router-dom';
import SbItem from './SbItem' import SbItem from './SbItem'
import {SbContainer} from './Sb.style' import {SbContainer, VwDiv, VwPtag} from './Sb.style'
import Row from 'react-bootstrap/Row'; import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col'; import Col from 'react-bootstrap/Col';
import * as EgovNet from 'api/egovFetch'; import * as EgovNet from 'api/egovFetch';
function CodeViewer(props) { function CodeViewer(props) {
const {linkedDocCode} = useParams();
const [{docCode}, setDocCode] = useState(useParams()); const [docCode, setDocCode] = useState(linkedDocCode !== undefined?linkedDocCode:props.docCode);
const [docName, setDocName] = useState(props.docName);
const [codeTree, setCodeTree] = useState(); const [codeTree, setCodeTree] = useState();
const [docSummary, setDocSummary] = useState(); const [docSummary, setDocSummary] = useState();
const [docDetail, setDocDetail] = useState(); const [docDetail, setDocDetail] = useState();
@ -20,22 +21,22 @@ function CodeViewer(props) {
console.log("viewer [location] : ", location); console.log("viewer [location] : ", location);
console.log("viewer [docCode] : ", docCode); console.log("viewer [docCode] : ", docCode);
const updateDocCode = (docCode, docName)=>{
setDocCode(docCode);
setDocName(docName);
getCodeDetailInfo(docCode);
}
const retrieveList = useCallback(() => { const getCodeTree = ()=>{
console.groupCollapsed("EgovMain.retrieveList()"); EgovNet.requestFetch(
const retrieveListURL = '/standardCode/viewer.do'; '/standardCode/getCodeTree.do',
const requestOptions = { {
method: "POST", method: "POST",
headers: { headers: {
'Content-type': 'application/json' 'Content-type': 'application/json'
},
body: JSON.stringify({})
}, },
body: JSON.stringify({
docCode: docCode
})
}
EgovNet.requestFetch(retrieveListURL,
requestOptions,
(resp) => { (resp) => {
const menuData = resp.result.codeTree; const menuData = resp.result.codeTree;
// 코드 목록 트리 구성 // 코드 목록 트리 구성
@ -48,8 +49,8 @@ function CodeViewer(props) {
if(tree.length>0){ if(tree.length>0){
treeTag.push( treeTag.push(
<SbContainer> <SbContainer>
{tree.map((subItem, index) => {tree.map((subItem) =>
<SbItem item={subItem} key={index} /> <SbItem item={subItem} openDocCode={docCode} updateDocCode={updateDocCode} />
)} )}
</SbContainer> </SbContainer>
) )
@ -57,18 +58,59 @@ function CodeViewer(props) {
treeTag.push(<div>검색된 결과가 없습니다.</div>); // 코드 목록 초기값 treeTag.push(<div>검색된 결과가 없습니다.</div>); // 코드 목록 초기값
} }
setCodeTree(treeTag); setCodeTree(treeTag);
},
function (resp) {
console.log("err response : ", resp);
}
);
}
const getCodeDetailInfo = useCallback((docCode) => {
console.groupCollapsed("EgovMain.getCodeDetailInfo()");
EgovNet.requestFetch(
'/standardCode/getCodeDetailInfo.do',
{
method: "POST",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({
docCode: docCode
})
},
(resp) => {
// 목차 구성 // 목차 구성
let summaryTag = []; let summaryTag = [];
// 문서 전문 구성 // 문서 전문 구성
let detailTag = []; let detailTag = [];
if(resp.result.document.length>0){ if(resp.result.document.length>0){
const reg = /([A-Z]{3,5}(\s[0-9]{2}){3,4})/g
resp.result.document.forEach(function (item, index){ resp.result.document.forEach(function (item, index){
summaryTag.push( const isTitle = item.full_content.includes(item.group_title);
<div>{item.group_title}</div> if(isTitle){
) summaryTag.push(
<VwDiv depth={item.cont_level} isTitle={isTitle}>
<VwPtag href="#" isTitle={isTitle} onClick={() => {
document.location.hash="#"+item.cont_type_cd;
}}>
{item.group_title}
</VwPtag>
</VwDiv>
)
}
if(reg.test(item.full_content)){
const docCodeAry = item.full_content.match(reg);
const docCodeLink = [];
for(let i=0; i<docCodeAry.length; i++) {
docCodeLink.push('<a class="docLink" href="/standardCode/viewer/' + docCode + '" target="_blank">' + docCode + '</a>')
}
for(let i=0; i<docCodeAry.length; i++){
const docCode = docCodeAry[i];
item.full_content = item.full_content.replaceAll(docCode, docCodeLink[i]);
}
}
detailTag.push( detailTag.push(
<div dangerouslySetInnerHTML={ {__html: item.full_content} }></div> <VwDiv depth={item.cont_level} isTitle={isTitle} id={item.cont_type_cd} dangerouslySetInnerHTML={ {__html: item.full_content} }></VwDiv>
) )
}) })
}else{ }else{
@ -81,30 +123,41 @@ function CodeViewer(props) {
console.log("err response : ", resp); console.log("err response : ", resp);
} }
); );
console.groupEnd("EgovMain.retrieveList()"); console.groupEnd("EgovMain.getCodeDetailInfo()");
},[]); },[]);
useEffect(() => { useEffect(() => {
retrieveList(); getCodeTree();
}, [retrieveList]); getCodeDetailInfo(docCode);
}, []);
console.log("------------------------------viewer [End]"); console.log("------------------------------viewer [End]");
console.groupEnd("viewer"); console.groupEnd("viewer");
return ( return (
<Row> <Row className="mx-0">
<Col xs={3} className="border-end"> <Col xs={12} className="border-bottom">
<Row>
<Col xs={3}></Col>
<Col xs={9}>{docCode} {docName}</Col>
</Row>
</Col>
<Col xs={3} className="border-end viewerDiv">
{codeTree} {codeTree}
</Col> </Col>
<Col xs={3} className="border-end"> <Col xs={3} className="border-end viewerDiv">
{docSummary} {docSummary}
</Col> </Col>
<Col xs={6}> <Col xs={6} className="viewerDiv">
{docDetail} {docDetail}
</Col> </Col>
</Row> </Row>
); );
} }
CodeViewer.defaultProps = {
docCode: 'KDS 21 45 00',
docName: '가설교량 및 노면복공 설계기준'
}
export default CodeViewer; export default CodeViewer;

View File

@ -225,8 +225,9 @@ const SecondRoutes = () => {
{/* 사이트관리자 암호 바꾸기 기능 추가 2023.04.15(토) 김일국 */} {/* 사이트관리자 암호 바꾸기 기능 추가 2023.04.15(토) 김일국 */}
<Route path={URL.ADMIN_MANAGER} element={<EgovAdminPasswordUpdate />} /> <Route path={URL.ADMIN_MANAGER} element={<EgovAdminPasswordUpdate />} />
{/*기준코드*/} {/*기준코드 뷰어*/}
<Route path={URL.STANDARD_CODE_VIEWER} element={<CodeViewer mode={CODE.MODE_READ} />} /> <Route path={URL.STANDARD_CODE_VIEWER} element={<CodeViewer mode={CODE.MODE_READ} />} />
<Route path={URL.STANDARD_CODE_VIEWER_LINK} element={<CodeViewer mode={CODE.MODE_READ} />} />
</Routes> </Routes>
<EgovFooter /> <EgovFooter />