
--- client/views/component/autosearch/AutoSearch.jsx
+++ client/views/component/autosearch/AutoSearch.jsx
... | ... | @@ -14,6 +14,15 @@ |
14 | 14 |
selectClientName(""); |
15 | 15 |
}, []); |
16 | 16 |
|
17 |
+ useEffect(() => { |
|
18 |
+ if (props.CUSTCD && accounts.length > 0) { |
|
19 |
+ const matchedAccount = accounts.find(account => account.CUSTCD === props.CUSTCD); |
|
20 |
+ if (matchedAccount) { |
|
21 |
+ setInputValue(matchedAccount.JSNAME); |
|
22 |
+ } |
|
23 |
+ } |
|
24 |
+ }, [props.CUSTCD, accounts]); |
|
25 |
+ |
|
17 | 26 |
const selectClientName = (name) => { |
18 | 27 |
axios({ |
19 | 28 |
method: 'post', |
... | ... | @@ -39,25 +48,34 @@ |
39 | 48 |
const handleInputChange = (e) => { |
40 | 49 |
const value = e.target.value; |
41 | 50 |
setInputValue(value); |
42 |
- |
|
43 |
- const filtered = accounts.filter((account) => |
|
44 |
- account.JSNAME.toLowerCase().includes(value.toLowerCase()) |
|
45 |
- ); |
|
46 |
- setAccountResults(filtered); |
|
47 |
- |
|
48 |
- if (value === "" || filtered.length === 0) { |
|
51 |
+ |
|
52 |
+ if (value === "") { |
|
53 |
+ props.setCUSTCD(""); |
|
54 |
+ setAccountResults([]); |
|
49 | 55 |
list.style.display = "none"; |
50 | 56 |
list.style.padding = "0"; |
51 | 57 |
list.style.backgroundColor = "transparent"; |
52 | 58 |
list.style.border = "0"; |
53 | 59 |
} else { |
54 |
- list.style.display = "block"; |
|
55 |
- list.style.padding = "10px"; |
|
56 |
- list.style.backgroundColor = "#ffffff"; |
|
57 |
- list.style.border = "1px solid #ddd"; |
|
60 |
+ const filtered = accounts.filter((account) => |
|
61 |
+ account.JSNAME.toLowerCase().includes(value.toLowerCase()) |
|
62 |
+ ); |
|
63 |
+ setAccountResults(filtered); |
|
64 |
+ |
|
65 |
+ if (filtered.length === 0) { |
|
66 |
+ list.style.display = "none"; |
|
67 |
+ list.style.padding = "0"; |
|
68 |
+ list.style.backgroundColor = "transparent"; |
|
69 |
+ list.style.border = "0"; |
|
70 |
+ } else { |
|
71 |
+ list.style.display = "block"; |
|
72 |
+ list.style.padding = "10px"; |
|
73 |
+ list.style.backgroundColor = "#ffffff"; |
|
74 |
+ list.style.border = "1px solid #ddd"; |
|
75 |
+ } |
|
58 | 76 |
} |
59 | 77 |
}; |
60 |
- |
|
78 |
+ |
|
61 | 79 |
const handleItemClick = (account) => { |
62 | 80 |
setInputValue(account.JSNAME); |
63 | 81 |
props.setCUSTCD(account.CUSTCD); |
--- client/views/layout/Header.jsx
+++ client/views/layout/Header.jsx
... | ... | @@ -6,11 +6,7 @@ |
6 | 6 |
function Header(props) { |
7 | 7 |
const navigate = useNavigate(); |
8 | 8 |
const rollBackBtn = () => { |
9 |
- if (props.pathName === "영업 일지"){ |
|
10 |
- navigate("/"); |
|
11 |
- } else{ |
|
12 |
- navigate(-1); // 바로 이전 페이지로 이동 |
|
13 |
- } |
|
9 |
+ navigate(-1); |
|
14 | 10 |
}; |
15 | 11 |
return ( |
16 | 12 |
<header> |
--- client/views/layout/Menu.jsx
+++ client/views/layout/Menu.jsx
... | ... | @@ -1,4 +1,4 @@ |
1 |
-import React, { useState } from "react"; |
|
1 |
+import React, { useState, useEffect } from "react"; |
|
2 | 2 |
import { Link, useLocation } from "react-router-dom"; |
3 | 3 |
import Icon from "@mdi/react"; |
4 | 4 |
import { |
... | ... | @@ -35,27 +35,25 @@ |
35 | 35 |
const [activeIndex, setActiveIndex] = useState(null); |
36 | 36 |
const location = useLocation(); |
37 | 37 |
|
38 |
- const handleClick = (index) => { |
|
39 |
- setActiveIndex(index); |
|
40 |
- }; |
|
41 |
- |
|
42 |
- const isActive = (path) => location.pathname === path; |
|
38 |
+ useEffect(() => { |
|
39 |
+ const currentIndex = menuList.findIndex(menu => menu.path === location.pathname); |
|
40 |
+ setActiveIndex(currentIndex); |
|
41 |
+ }, [location]); |
|
43 | 42 |
|
44 | 43 |
return ( |
45 | 44 |
<nav className="main-menu"> |
46 |
- <ul className="flex justify-center aling-center"> |
|
45 |
+ <ul className="flex justify-center align-center"> |
|
47 | 46 |
{menuList.map((menu, index) => ( |
48 | 47 |
<li |
48 |
+ key={index} |
|
49 | 49 |
className={`${ |
50 |
- activeIndex === index || isActive(menu.path) |
|
51 |
- ? "active pl5 pr5" |
|
52 |
- : "pl5 pr5" |
|
50 |
+ activeIndex === index ? "active pl5 pr5" : "pl5 pr5" |
|
53 | 51 |
}`} |
54 |
- onClick={() => handleClick(index)} |
|
55 | 52 |
> |
56 | 53 |
<Link |
57 | 54 |
to={menu.path} |
58 | 55 |
className="flex-column justify-center align-center" |
56 |
+ onClick={() => setActiveIndex(index)} |
|
59 | 57 |
> |
60 | 58 |
<p>{menu.icon}</p> |
61 | 59 |
<p>{menu.pathName}</p> |
--- client/views/pages/main/HomeInsert.jsx
+++ client/views/pages/main/HomeInsert.jsx
... | ... | @@ -18,8 +18,17 @@ |
18 | 18 |
React.useEffect(() => { |
19 | 19 |
if (props.insertTrigger) { |
20 | 20 |
insertNote(); |
21 |
+ sessionStorage.removeItem("data"); |
|
22 |
+ sessionStorage.removeItem("index"); |
|
23 |
+ sessionStorage.removeItem("scrollPosition"); |
|
24 |
+ sessionStorage.removeItem("searchScroll"); |
|
25 |
+ sessionStorage.removeItem("searchData"); |
|
26 |
+ sessionStorage.removeItem("searchState"); |
|
27 |
+ |
|
28 |
+ setTimeout(() => { |
|
21 | 29 |
props.setInsertTrigger(false); |
22 |
- navigate("/"); |
|
30 |
+ navigate("/"); |
|
31 |
+ }, 1000); |
|
23 | 32 |
} |
24 | 33 |
}, [props.insertTrigger, props.setInsertTrigger]); |
25 | 34 |
|
--- client/views/pages/main/HomeSelectList.jsx
+++ client/views/pages/main/HomeSelectList.jsx
... | ... | @@ -6,7 +6,6 @@ |
6 | 6 |
mdiHandshakeOutline, |
7 | 7 |
mdiFileCheckOutline, |
8 | 8 |
mdiAccountOutline, |
9 |
- mdiChatOutline, |
|
10 | 9 |
} from "@mdi/js"; |
11 | 10 |
import axios from "axios"; |
12 | 11 |
|
... | ... | @@ -14,18 +13,65 @@ |
14 | 13 |
const [data, setData] = React.useState([]); |
15 | 14 |
const [index, setIndex] = React.useState("1"); |
16 | 15 |
const [hasMore, setHasMore] = React.useState(true); |
16 |
+ const [isLoading, setIsLoading] = React.useState(true); // 데이터 로딩 상태 변수 추가 |
|
17 | 17 |
|
18 | 18 |
const navigate = useNavigate(); |
19 |
+ const scrollRef = React.useRef(null); |
|
20 |
+ |
|
21 |
+ // 이미 세션에 데이터가 저장되었다면 가져오고 아니면 함수 호출 |
|
22 |
+ useEffect(() => { |
|
23 |
+ const savedData = sessionStorage.getItem("data"); |
|
24 |
+ const savedIndex = sessionStorage.getItem("index"); |
|
25 |
+ if (savedData && savedIndex) { |
|
26 |
+ setData(JSON.parse(savedData)); |
|
27 |
+ setIndex(JSON.parse(savedIndex)); |
|
28 |
+ setIsLoading(false); |
|
29 |
+ } else { |
|
30 |
+ setIsLoading(false); |
|
31 |
+ if (hasMore) { |
|
32 |
+ selectNote(); |
|
33 |
+ } |
|
34 |
+ } |
|
35 |
+ }, []); |
|
36 |
+ |
|
37 |
+ // 데이터가 변경되면 세션에 저장 |
|
38 |
+ useEffect(() => { |
|
39 |
+ sessionStorage.setItem("data", JSON.stringify(data)); |
|
40 |
+ sessionStorage.setItem("index", JSON.stringify(index)); |
|
41 |
+ if (data.length > 0) { |
|
42 |
+ restoreScrollPosition(); |
|
43 |
+ } |
|
44 |
+ }, [data, index, hasMore]); |
|
45 |
+ |
|
46 |
+ // 스크롤 데이터가 바꿔지면 세션에 저장 |
|
47 |
+ const restoreScrollPosition = () => { |
|
48 |
+ const savedScrollPosition = sessionStorage.getItem("scrollPosition"); |
|
49 |
+ if (savedScrollPosition && scrollRef.current) { |
|
50 |
+ scrollRef.current.scrollTo(0, parseInt(savedScrollPosition, 10)); |
|
51 |
+ } |
|
52 |
+ }; |
|
53 |
+ |
|
54 |
+ const handleScroll = () => { |
|
55 |
+ if (scrollRef.current) { |
|
56 |
+ const { scrollTop } = scrollRef.current; |
|
57 |
+ sessionStorage.setItem("scrollPosition", scrollTop.toString()); |
|
58 |
+ } |
|
59 |
+ }; |
|
19 | 60 |
|
20 | 61 |
useEffect(() => { |
21 |
- selectNote(); |
|
22 |
- }, []); |
|
62 |
+ const currentScrollRef = scrollRef.current; |
|
63 |
+ currentScrollRef.addEventListener("scroll", handleScroll); |
|
64 |
+ |
|
65 |
+ return () => { |
|
66 |
+ currentScrollRef.removeEventListener("scroll", handleScroll); |
|
67 |
+ }; |
|
68 |
+ }, [scrollRef.current]); |
|
23 | 69 |
|
24 | 70 |
// 무한 스크롤 적용 |
25 | 71 |
useEffect(() => { |
26 | 72 |
const observer = new IntersectionObserver( |
27 | 73 |
(entries) => { |
28 |
- if (entries[0].isIntersecting) { |
|
74 |
+ if (entries[0].isIntersecting && !isLoading) { |
|
29 | 75 |
if (hasMore) { |
30 | 76 |
selectNote(); |
31 | 77 |
} |
... | ... | @@ -41,7 +87,7 @@ |
41 | 87 |
return () => { |
42 | 88 |
if (lastElement) observer.unobserve(lastElement); |
43 | 89 |
}; |
44 |
- }, [data, hasMore]); |
|
90 |
+ }, [data, isLoading]); |
|
45 | 91 |
|
46 | 92 |
// 상세 조회 이동 |
47 | 93 |
const handleItemClick = (notecd) => { |
... | ... | @@ -51,7 +97,7 @@ |
51 | 97 |
// 영업 일지 조회 API |
52 | 98 |
const selectNote = () => { |
53 | 99 |
if (!props.SABNHO) { |
54 |
- return; |
|
100 |
+ return ; |
|
55 | 101 |
} |
56 | 102 |
axios({ |
57 | 103 |
method: "post", |
... | ... | @@ -71,10 +117,7 @@ |
71 | 117 |
} else { |
72 | 118 |
const transformedData = response.data.resultData.map((item) => { |
73 | 119 |
const vsdate = item.NOTELIST.VSDATE; |
74 |
- const formattedVsdate = `${vsdate.slice(0, 4)}.${vsdate.slice( |
|
75 |
- 4, |
|
76 |
- 6 |
|
77 |
- )}.${vsdate.slice(6, 8)}`; |
|
120 |
+ const formattedVsdate = `${vsdate.slice(0, 4)}.${vsdate.slice(4, 6)}.${vsdate.slice(6, 8)}`; |
|
78 | 121 |
const tags = item.NOTELIST.HASTAG |
79 | 122 |
? item.NOTELIST.HASTAG.split("#").filter((tag) => tag) |
80 | 123 |
: []; |
... | ... | @@ -99,7 +142,10 @@ |
99 | 142 |
}; |
100 | 143 |
|
101 | 144 |
return ( |
102 |
- <div className="main-page"> |
|
145 |
+ <div |
|
146 |
+ className="main-page" |
|
147 |
+ ref={scrollRef} |
|
148 |
+ style={{ overflow: "auto", height: "100vh" }} > |
|
103 | 149 |
<ul> |
104 | 150 |
{Array.isArray(data) && |
105 | 151 |
data.map((item, index) => ( |
--- client/views/pages/main/HomeSelectOne.jsx
+++ client/views/pages/main/HomeSelectOne.jsx
... | ... | @@ -38,8 +38,17 @@ |
38 | 38 |
React.useEffect(() => { |
39 | 39 |
if (props.deleteTrigger) { |
40 | 40 |
deleteNote(); |
41 |
+ sessionStorage.removeItem("data"); |
|
42 |
+ sessionStorage.removeItem("index"); |
|
43 |
+ sessionStorage.removeItem("scrollPosition"); |
|
44 |
+ sessionStorage.removeItem("searchScroll"); |
|
45 |
+ sessionStorage.removeItem("searchData"); |
|
46 |
+ sessionStorage.removeItem("searchState"); |
|
47 |
+ |
|
48 |
+ setTimeout(() => { |
|
41 | 49 |
props.setDeleteTrigger(false); |
42 |
- navigate("/"); |
|
50 |
+ navigate("/"); |
|
51 |
+ }, 1000); |
|
43 | 52 |
} |
44 | 53 |
}, [props.deleteTrigger, props.setDeleteTrigger]); |
45 | 54 |
|
... | ... | @@ -99,6 +108,7 @@ |
99 | 108 |
}) |
100 | 109 |
.then((response) => { |
101 | 110 |
props.onModal("해당 일지를 삭제했습니다"); |
111 |
+ |
|
102 | 112 |
}) |
103 | 113 |
.catch((error) => { |
104 | 114 |
props.onModal("오류 발생. 관리자에 문의해주세요"); |
... | ... | @@ -128,7 +138,7 @@ |
128 | 138 |
var minute = input.slice(10, 12); |
129 | 139 |
comments[i].CRTYMD = year + ". " + month + ". " + day + " " + hour + ":" + minute + " 등록됨"; |
130 | 140 |
|
131 |
- if(comments[i].MDFYMD){ |
|
141 |
+ if (comments[i].MDFYMD) { |
|
132 | 142 |
var input = comments[i].MDFYMD; |
133 | 143 |
var year = input.slice(0, 4); |
134 | 144 |
var month = input.slice(4, 6); |
... | ... | @@ -136,7 +146,7 @@ |
136 | 146 |
var hour = input.slice(8, 10); |
137 | 147 |
var minute = input.slice(10, 12); |
138 | 148 |
comments[i].MDFYMD = year + ". " + month + ". " + day + " " + hour + ":" + minute + " 수정됨"; |
139 |
- } |
|
149 |
+ } |
|
140 | 150 |
} |
141 | 151 |
setComments(comments); |
142 | 152 |
} |
... | ... | @@ -153,7 +163,7 @@ |
153 | 163 |
return; |
154 | 164 |
} |
155 | 165 |
|
156 |
- if (!comment || comment.trim() === '') { |
|
166 |
+ if (!comment || comment.trim() === '') { |
|
157 | 167 |
props.onModal("댓글을 입력해주세요."); |
158 | 168 |
return; |
159 | 169 |
} |
... | ... | @@ -192,7 +202,7 @@ |
192 | 202 |
url: url, |
193 | 203 |
}) |
194 | 204 |
.then((response) => { |
195 |
- selectComment(); |
|
205 |
+ selectComment(); |
|
196 | 206 |
|
197 | 207 |
}) |
198 | 208 |
.catch((error) => { |
--- client/views/pages/search/Search.jsx
+++ client/views/pages/search/Search.jsx
... | ... | @@ -1,4 +1,4 @@ |
1 |
-import React from "react"; |
|
1 |
+import React, { useEffect } from "react"; |
|
2 | 2 |
import AutoSearch from "./../../component/autosearch/AutoSearch.jsx"; |
3 | 3 |
import Icon from "@mdi/react"; |
4 | 4 |
import { |
... | ... | @@ -21,11 +21,73 @@ |
21 | 21 |
const [hastag, setHastag] = React.useState(""); |
22 | 22 |
|
23 | 23 |
const navigate = useNavigate(); |
24 |
+ const scrollRef = React.useRef(null); |
|
24 | 25 |
|
25 | 26 |
const handleItemClick = (notecd) => { |
26 | 27 |
navigate(`/homeSelectOne?notecd=${notecd}`); |
27 | 28 |
}; |
28 | 29 |
|
30 |
+ // 이미 세션에 데이터가 저장되었다면 가져오고 아니면 함수 호출 |
|
31 |
+ useEffect(() => { |
|
32 |
+ const savedState = JSON.parse(sessionStorage.getItem('searchState')); |
|
33 |
+ const savedData = JSON.parse(sessionStorage.getItem('searchData')); |
|
34 |
+ |
|
35 |
+ if (savedState) { |
|
36 |
+ setCUSTCD(savedState.CUSTCD); |
|
37 |
+ setStdate(savedState.stdate); |
|
38 |
+ setEndate(savedState.endate); |
|
39 |
+ setContsx(savedState.contsx); |
|
40 |
+ setHastag(savedState.hastag); |
|
41 |
+ } |
|
42 |
+ if (savedData){ |
|
43 |
+ setData(savedData); |
|
44 |
+ } |
|
45 |
+ }, []); |
|
46 |
+ |
|
47 |
+ // 데이터가 변경되면 세션에 저장 |
|
48 |
+ useEffect(() => { |
|
49 |
+ const searchState = { |
|
50 |
+ CUSTCD, |
|
51 |
+ stdate, |
|
52 |
+ endate, |
|
53 |
+ contsx, |
|
54 |
+ hastag, |
|
55 |
+ scrollPosition: window.scrollY |
|
56 |
+ }; |
|
57 |
+ |
|
58 |
+ sessionStorage.setItem("searchState", JSON.stringify(searchState)); |
|
59 |
+ sessionStorage.setItem("searchData", JSON.stringify(data)); |
|
60 |
+ if (data.length > 0) { |
|
61 |
+ restoreScrollPosition(); |
|
62 |
+ } |
|
63 |
+ |
|
64 |
+ }, [CUSTCD, stdate, endate, contsx, hastag, data]); |
|
65 |
+ |
|
66 |
+ // 스크롤 데이터가 바꿔지면 세션에 저장 |
|
67 |
+ const restoreScrollPosition = () => { |
|
68 |
+ const savedScrollPosition = sessionStorage.getItem("searchScroll"); |
|
69 |
+ if (savedScrollPosition && scrollRef.current) { |
|
70 |
+ scrollRef.current.scrollTo(0, parseInt(savedScrollPosition, 10)); |
|
71 |
+ } |
|
72 |
+ }; |
|
73 |
+ |
|
74 |
+ const handleScroll = () => { |
|
75 |
+ if (scrollRef.current) { |
|
76 |
+ const { scrollTop } = scrollRef.current; |
|
77 |
+ sessionStorage.setItem("searchScroll", scrollTop.toString()); |
|
78 |
+ } |
|
79 |
+ }; |
|
80 |
+ |
|
81 |
+ useEffect(() => { |
|
82 |
+ const currentScrollRef = scrollRef.current; |
|
83 |
+ currentScrollRef.addEventListener("scroll", handleScroll); |
|
84 |
+ |
|
85 |
+ return () => { |
|
86 |
+ currentScrollRef.removeEventListener("scroll", handleScroll); |
|
87 |
+ }; |
|
88 |
+ }, [scrollRef.current]); |
|
89 |
+ |
|
90 |
+ |
|
29 | 91 |
// 영업 일지 조회 API |
30 | 92 |
const selectNote = () => { |
31 | 93 |
if (!props.SABNHO) { |
... | ... | @@ -61,13 +123,8 @@ |
61 | 123 |
if (response.data && response.data.resultCode === 200) { |
62 | 124 |
const transformedData = response.data.resultData.map((item) => { |
63 | 125 |
const vsdate = item.NOTELIST.VSDATE; |
64 |
- const formattedVsdate = `${vsdate.slice(0, 4)}.${vsdate.slice( |
|
65 |
- 4, |
|
66 |
- 6 |
|
67 |
- )}.${vsdate.slice(6, 8)}`; |
|
68 |
- const tags = item.NOTELIST.HASTAG |
|
69 |
- ? item.NOTELIST.HASTAG.split("#").filter((tag) => tag) |
|
70 |
- : []; |
|
126 |
+ const formattedVsdate = `${vsdate.slice(0, 4)}.${vsdate.slice(4, 6)}.${vsdate.slice(6, 8)}`; |
|
127 |
+ const tags = item.NOTELIST.HASTAG ? item.NOTELIST.HASTAG.split("#").filter((tag) => tag) : []; |
|
71 | 128 |
return { |
72 | 129 |
...item, |
73 | 130 |
NOTELIST: { |
... | ... | @@ -86,11 +143,13 @@ |
86 | 143 |
}; |
87 | 144 |
|
88 | 145 |
return ( |
89 |
- <div className="search-page"> |
|
146 |
+ <div className="search-page" ref={scrollRef} |
|
147 |
+ style={{ overflow: "auto", height: "100vh" }} > |
|
90 | 148 |
<div className="search-zone mb10"> |
91 | 149 |
<div className="input-group mb10"> |
92 | 150 |
<AutoSearch |
93 | 151 |
titleName={"거래처"} |
152 |
+ CUSTCD={CUSTCD} |
|
94 | 153 |
setCUSTCD={setCUSTCD} |
95 | 154 |
SABNHO={props.SABNHO} |
96 | 155 |
/> |
... | ... | @@ -154,7 +213,7 @@ |
154 | 213 |
</div> |
155 | 214 |
</div> |
156 | 215 |
|
157 |
- <div className="main-page"> |
|
216 |
+ <div className="main-page" > |
|
158 | 217 |
<ul> |
159 | 218 |
{Array.isArray(data) && |
160 | 219 |
data.map((item, index) => ( |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?