فهرست منبع

고객센터 페이지 SPA 작업중

inkoreakim 2 سال پیش
والد
کامیت
1eb5bbdc85

+ 9 - 5
src/main/java/com/example/ili_web/board/controller/BoardController.java

@@ -10,6 +10,9 @@ import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
+import java.util.List;
+
 @Controller
 @Slf4j
 @RequestMapping("/api/v1")
@@ -67,11 +70,12 @@ public class BoardController {
      date -> 2024.03.14
      **/
     @PostMapping("/board/add")
-    public ResponseEntity insertBoard (@RequestBody BoardInfoReq req, @RequestParam("file") MultipartFile file) {
+    public ResponseEntity insertBoard (BoardInfoReq req, @RequestPart("files") List<MultipartFile> files) throws IOException {
         log.info("========== Insert Board Data ==========");
-        log.info("data = " + req.getTitle());
-        log.info("data = " + req.getUserName());
-        log.info("data = " + file.getContentType());
-        return null;
+        for (MultipartFile file : files) {
+            log.info("file =>" + file.getOriginalFilename());
+        }
+        boardService.insertBoard(req, files);
+        return ResponseEntity.ok(null);
     }
 }

+ 18 - 17
src/main/java/com/example/ili_web/board/domain/Board.java

@@ -1,15 +1,15 @@
 package com.example.ili_web.board.domain;
 
+import com.example.ili_web.board.dto.BoardInfoReq;
+import com.example.ili_web.board.dto.BoardReq;
 import jakarta.persistence.*;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
+import lombok.*;
 
 import java.time.LocalDateTime;
 
 @Getter
 @Entity
-@NoArgsConstructor
+@NoArgsConstructor( access = AccessLevel.PROTECTED)
 @Table(name="BOARD")
 public class Board {
 
@@ -40,18 +40,19 @@ public class Board {
     private LocalDateTime upDt;
 
     @Builder
-    public Board(Long id, String userName, String password, String company, String phoneNum, String nickName, String title, String content, String type, String useYn, LocalDateTime regDt, LocalDateTime upDt) {
-        this.id = id;
-        this.userName = userName;
-        this.password = password;
-        this.company = company;
-        this.phoneNum = phoneNum;
-        this.nickName = nickName;
-        this.title = title;
-        this.content = content;
-        this.type = type;
-        this.useYn = useYn;
-        this.regDt = regDt;
-        this.upDt = upDt;
+    public Board(BoardInfoReq req) {
+        this.userName = req.getUserName();
+        this.password = req.getPassword();
+        this.company = req.getCompany();
+        this.phoneNum = req.getPhone();
+        this.nickName = req.getNickname();
+        this.title = req.getTitle();
+        this.content = req.getContent();
+        this.type = req.getType();
+        this.useYn = "Y";
+        this.regDt = LocalDateTime.now();
+        this.upDt = LocalDateTime.now();
     }
+
+
 }

+ 49 - 0
src/main/java/com/example/ili_web/board/service/BoardService.java

@@ -1,5 +1,6 @@
 package com.example.ili_web.board.service;
 
+import com.example.ili_web.board.domain.Board;
 import com.example.ili_web.board.dto.*;
 import com.example.ili_web.board.repository.BoardRepository;
 import com.example.ili_web.board.repository.BoardRepositoryDsl;
@@ -9,11 +10,20 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.client.reactive.ReactorClientHttpConnector;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.reactive.function.client.WebClient;
 import org.springframework.web.reactive.function.client.WebClient;
 import reactor.netty.http.client.HttpClient;
 import org.springframework.http.client.reactive.ReactorClientHttpConnector;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
 import static java.net.http.HttpClient.*;
 
 @Service
@@ -22,6 +32,10 @@ import static java.net.http.HttpClient.*;
 public class BoardService {
     private final BoardRepository repository;
     private final BoardRepositoryDsl boardDsl;
+    @Value("${multipart.file.storage.win}")
+    private String fileStorageWindow;
+    @Value("${multipart.file.storage.linux}")
+    private String fileStorageLinux;
     @Value("${global.message.sms.address}")
     private String address;
     @Value("${global.message.sms.url}")
@@ -82,4 +96,39 @@ public class BoardService {
     public CheckDupleRes checkNickName(BoardInfoReq req) {
         return new CheckDupleRes(repository.countBoardsByNickName(req.getNickname()));
     }
+
+    @Transactional
+    public void insertBoard(BoardInfoReq req, List<MultipartFile> file) throws IOException {
+        Board board = new Board(req);
+        repository.save(board);
+        String osPath = osProperty();
+        if (!file.isEmpty()) {
+            String filePath = osPath + "/" + req.getType() + "/Board" + board.getId()+"/";
+            fileUpload(filePath, file);
+        }
+    }
+
+    public String osProperty() {
+        String os = System.getProperty("os.name").toLowerCase();
+        String result;
+        if (os.contains("win")) {
+            result = fileStorageWindow;
+        } else {
+            result = fileStorageLinux;
+        }
+        return result;
+    }
+
+    public void fileUpload(String path, List<MultipartFile> files) throws IOException {
+        File dir = new File(path);
+        if (!dir.exists()) {
+            dir.mkdir();
+        }
+        for (MultipartFile file : files) {
+            byte[] bytes = file.getBytes();
+            Path realPath = Paths.get(path + file.getOriginalFilename());
+            Files.createDirectories(realPath.getParent());
+            Files.write(realPath, bytes);
+        }
+    }
 }

+ 6 - 0
src/main/java/com/example/ili_web/main/controller/MainController.java

@@ -68,4 +68,10 @@ public class MainController {
         log.info("========== Move to info-plaza ==========");
         return HTML_PATH + "contact-us.html";
     }
+
+    @GetMapping("/logi/advice")
+    public String test () {
+        log.info("========== Move to info-asd ==========");
+        return HTML_PATH + "contact-inner/logi-advice.html";
+    }
 }

+ 14 - 1
src/main/resources/application.properties

@@ -19,4 +19,17 @@ server.port=80
 global.message.sms.address=http://211.230.149.175
 global.message.sms.url=/Remote/RemoteSms.html
 global.message.sms.id=intl2000
-global.message.sms.pass=972179
+global.message.sms.pass=972179
+
+#multipart config
+spring.servlet.multipart.enabled=true
+spring.servlet.multipart.max-file-size=100MB
+spring.servlet.multipart.max-request-size=100MB
+spring.http.multipart.enabled=true
+
+#file root
+#window
+multipart.file.storage.win=C:\\data\\ili_web
+#linux
+multipart.file.storage.linux=/data/ili_web
+

+ 12 - 0
src/main/resources/static/js/business-sector/bizChangeInner.js

@@ -3,4 +3,16 @@ function changeInner (name) {
     const anotherElement = document.getElementsByClassName('container marketing');
     for (let div of anotherElement) div.style.display = 'none';
     element.style.display = 'block'
+}
+
+async function changeInnerAjax(name) {
+    const element = name !== 'logi-advice' ? document.getElementById(`${name}`) : document.getElementById('logi_advice')
+    const anotherElement = document.getElementsByClassName('container marketing');
+    for (let div of anotherElement) div.style.display = 'none';
+    element.style.display='block';
+    await axios.get(`/html/contact-inner/${name}.html`)
+        .then((res)=> {
+            //element.insertAdjacentHTML('afterBegin', res.request.responseText);
+            element.innerHTML = res.data;
+        })
 }

+ 56 - 3
src/main/resources/static/js/contact/advice.js

@@ -1,3 +1,4 @@
+let fileArray = [];
 function openApply() {
     const manual = document.getElementById('advice_manual');
     const manualHead = document.getElementById('advice_manual_header');
@@ -25,6 +26,7 @@ async function dupleNickCheck() {
     }
 }
 
+
 function subForm() {
     if (validationCheck()) {
         const form = document.getElementById('logi_ad_form')
@@ -33,12 +35,63 @@ function subForm() {
         formData.append('phoneNum', document.getElementById('lg_phone').value);
         formData.append('email', document.getElementById('lg_email').value);
         formData.append('nickname', document.getElementById('lg_nickname').value);
-        axios.post('api/v1/board/add', formData, {headers : {'Content-Type' : 'multipart/form-data'}})
+        if (fileArray.length > 0) {
+            console.log(fileArray)
+            for (let file of fileArray) {
+                formData.append('files', file[0]);
+            }
+        }
+        axios.post('api/v1/board/add', formData)
             .then(function (res) {
-                console.log(res)
+                alert('신청이 완료되었습니다.');
+                clearApply();
+                clearModal();
             })
             .catch(function (e) {
                 console.log(e)
             })
     }
-}
+}
+
+function clearApply() {
+    const div = document.getElementById('advice_apply');
+    const inputElements = div.querySelectorAll('input');
+    const btnElements = div.querySelectorAll('button');
+    inputElements.forEach(input => {
+        input.value = '';
+    });
+    btnElements.forEach(btn=>{
+        btn.disabled = false;
+    })
+
+    const ul = document.getElementById('lg_file_list');
+    ul.innerHTML = '';
+    fileArray = [];
+}
+
+function clearModal() {
+    const form = document.getElementById('check_info');
+    const inputElements = form.querySelectorAll('input');
+    inputElements.forEach(input => {
+        input.value = '';
+    });
+
+    const timer = document.getElementById('timer');
+    timer.innerHTML = '';
+}
+
+document.getElementById('lg_file').addEventListener('change', (event)=>{
+    const files = event.target.files;
+    let html = '';
+    if (fileArray.length > 5) {
+        alert('파일은 5개까지 업로드 가능합니다.')
+    } else {
+        fileArray.push(files);
+    }
+    const ul = document.getElementById('lg_file_list');
+    const textNode = document.createTextNode(files[0].name);
+    const li = document.createElement('li');
+    li.setAttribute('class', 'list-group-item')
+    li.appendChild(textNode);
+    ul.appendChild(li);
+})

+ 2 - 1
src/main/resources/static/js/contact/getList.js

@@ -15,7 +15,8 @@ function goToDetail (title, userName, regDt, content) {
 
 document.addEventListener('DOMContentLoaded', async function () {
     //getList('NT', 1);
+    await changeInnerAjax('notice');
     const res = await getList('NT', 1)
-    makeTable(res.data.dataList)
+    makeTable(res.data.dataList);
     pagenation(res.data);
 });

+ 33 - 26
src/main/resources/templates/html/contact-inner/logi-advice.html

@@ -37,53 +37,60 @@
             <div class="container my-5">
                 <form id="logi_ad_form">
                     <input type="hidden" name="type" value="LG">
-                    <div class="row">
-                        <div class="col-md-6">
-                            <label class="form-label" for="lg_name">성명</label>
+                    <div class="row g-3">
+                        <div class="col-md-6 form-floating h-3">
                             <input class="form-control" id="lg_name" required type="text" name="userName" disabled>
+                            <label class="form-label" for="lg_name">성명</label>
                         </div>
                         <div class="col-md-6">
-                            <label class="form-label" for="lg_phone">핸드폰번호</label>
-                            <div class="input-group mb-3">
+                            <div class="input-group mb-3 form-floating">
                                 <input class="form-control" id="lg_phone" required type="tel" name="phoneNum" disabled>
+                                <label class="form-label" for="lg_phone">핸드폰번호</label>
                                 <button class="btn btn-outline-secondary" id="button-phone" type="button" onclick="openModal()">인증 확인</button>
                             </div>
                         </div>
-                        <div class="col-md-6">
-                            <label class="form-label" for="lg_password">비밀번호</label>
-                            <input class="form-control" id="lg_password" required type="password" name="password">
-                        </div>
-                        <div class="col-md-6">
-                            <label class="form-label" for="lg_email">이메일</label>
+                        <div class="col-md-6 form-floating">
                             <input class="form-control" id="lg_email" required type="email" name="email" disabled>
+                            <label class="form-label" for="lg_email">이메일</label>
                         </div>
-                        <div class="col-md-6">
-                            <label class="form-label" for="lg_password_confirm">비밀번호 확인</label>
-                            <input class="form-control" id="lg_password_confirm" required type="password">
-                        </div>
-                        <div class="col-md-6">
-                            <label class="form-label" for="lg_nickname">닉네임</label>
-                            <div class="input-group mb-3">
+                        <div class="col-md-6 ">
+                            <div class="input-group mb-3 form-floating">
                                 <input class="form-control" id="lg_nickname" name="nickname" required type="text">
+                                <label class="form-label" for="lg_nickname">닉네임</label>
                                 <button class="btn btn-outline-secondary" id="button-nickname" type="button"  onclick="dupleNickCheck()">중복 확인</button>
                             </div>
                         </div>
-                        <div class="col-md-6">
-                            <label class="form-label" for="lg_company">상호</label>
+                        <div class="col-md-6 form-floating">
+                            <input class="form-control" id="lg_password" required type="password" name="password">
+                            <label class="form-label" for="lg_password">비밀번호</label>
+                        </div>
+                        <div class="col-md-6 form-floating">
+                            <input class="form-control" id="lg_password_confirm" required type="password">
+                            <label class="form-label" for="lg_password_confirm">비밀번호 확인</label>
+                        </div>
+
+                        <div class="col-md-6 form-floating">
                             <input class="form-control" id="lg_company" name="company" type="text">
+                            <label class="form-label" for="lg_company">상호</label>
                         </div>
                         <div class="col-md-6">
-                            <label class="form-label" for="lg_file">첨부파일 등록</label>
-                            <input class="form-control" id="lg_file" type="file" name="file">
+                            <label class="btn btn-primary" for="lg_file">파일업로드</label>
+                            <input class="form-control" id="lg_file" type="file" style="display:none;" multiple>
+                        </div>
+                        <div class="col-md-6">
+                            <p>파일목록</p>
+                            <ul id="lg_file_list" class="list-group mb-3">
+
+                            </ul>
                         </div>
                     </div>
-                    <div class="mb-3">
-                        <label class="form-label" for="lg_title">제목</label>
+                    <div class="mb-3 form-floating">
                         <input class="form-control" id="lg_title" required name="title" type="text">
+                        <label class="form-label" for="lg_title">제목</label>
                     </div>
-                    <div class="mb-3">
+                    <div class="mb-3 form-floating">
+                        <textarea class="form-control" id="lg_content" required rows="10" name="content"></textarea>
                         <label class="form-label" for="lg_content">내용</label>
-                        <textarea class="form-control" id="lg_content" required rows="3" name="content"></textarea>
                     </div>
                     <button class="btn btn-primary" type="button" onclick="subForm()">신청하기</button>
                 </form>

+ 9 - 6
src/main/resources/templates/html/contact-us.html

@@ -35,9 +35,9 @@
                     <div class="carousel-caption">
                         <h1>고객센터</h1>
                         <p>The Master of Global Logistics</p>
-                        <p><a class="btn" href="javascript:void(0);" th:onclick="changeInner('notice')">안내/공지</a>
-                            <a class="btn" href="javascript:void(0);" th:onclick="changeInner('estimate')">물류견적</a>
-                            <a class="btn" href="javascript:void(0);" th:onclick="changeInner('logi_advice')">물류자문</a>
+                        <p><a class="btn" href="javascript:void(0);" th:onclick="changeInnerAjax('notice')">안내/공지</a>
+                            <a class="btn" href="javascript:void(0);" th:onclick="changeInnerAjax('estimate')">물류견적</a>
+                            <a class="btn" href="javascript:void(0);" th:onclick="changeInnerAjax('logi-advice')">물류자문</a>
                             <a class="btn" href="javascript:void(0);" th:onclick="changeInner('history')">무역자문</a>
                         </p>
                     </div>
@@ -47,11 +47,14 @@
     </div>
     <!-- 업무내용 설명 -->
 
-    <div class="container marketing" id="notice" th:include="html/contact-inner/notice">
+    <!--<div class="container marketing" id="notice" th:include="html/contact-inner/notice">-->
+    <div class="container marketing" id="notice">
     </div>
-    <div class="container marketing" id="estimate" th:include="html/contact-inner/estimate" style="display: none">
+   <!-- <div class="container marketing" id="estimate" th:include="html/contact-inner/estimate" style="display: none">-->
+    <div class="container marketing" id="estimate" style="display: none">
     </div>
-    <div class="container marketing" id="logi_advice" th:include="html/contact-inner/logi-advice" style="display: none">
+    <!--<div class="container marketing" id="logi_advice" th:include="html/contact-inner/logi-advice" style="display: none">-->
+    <div class="container marketing" id="logi_advice" style="display: none">
     </div>
     <hr class="featurette-divider">
 </main>