فهرست منبع

물류 자문 등록기능 구현중

inkoreakim 2 سال پیش
والد
کامیت
f0099ef1d7
27فایلهای تغییر یافته به همراه824 افزوده شده و 164 حذف شده
  1. 23 3
      build.gradle
  2. 76 1
      src/main/java/com/example/ili_web/board/controller/BoardController.java
  3. 56 1
      src/main/java/com/example/ili_web/board/domain/Board.java
  4. 13 1
      src/main/java/com/example/ili_web/board/dto/AdviceList.java
  5. 18 0
      src/main/java/com/example/ili_web/board/dto/BoardInfoReq.java
  6. 14 1
      src/main/java/com/example/ili_web/board/dto/BoardList.java
  7. 11 1
      src/main/java/com/example/ili_web/board/dto/BoardReq.java
  8. 12 1
      src/main/java/com/example/ili_web/board/dto/BoardRes.java
  9. 9 0
      src/main/java/com/example/ili_web/board/dto/SendSmsReq.java
  10. 14 0
      src/main/java/com/example/ili_web/board/dto/SuccessRes.java
  11. 7 1
      src/main/java/com/example/ili_web/board/repository/BoardRepository.java
  12. 34 7
      src/main/java/com/example/ili_web/board/repository/BoardRepositoryDsl.java
  13. 84 1
      src/main/java/com/example/ili_web/board/service/BoardService.java
  14. 16 1
      src/main/java/com/example/ili_web/config/QueryDslConfig.java
  15. 12 0
      src/main/java/com/example/ili_web/main/response/CheckDupleRes.java
  16. 12 3
      src/main/resources/application.properties
  17. 30 3
      src/main/resources/static/js/contact/advice.js
  18. 52 4
      src/main/resources/static/js/contact/adviceValidation.js
  19. 85 0
      src/main/resources/static/js/contact/auth-phone.js
  20. 1 15
      src/main/resources/static/js/contact/getList.js
  21. 4 0
      src/main/resources/static/js/contact/openModal.js
  22. 10 0
      src/main/resources/static/js/cookie/cookie-control.js
  23. 87 10
      src/main/resources/templates/html/contact-inner/estimate.html
  24. 86 53
      src/main/resources/templates/html/contact-inner/logi-advice.html
  25. 10 36
      src/main/resources/templates/html/contact-inner/notice.html
  26. 18 11
      src/main/resources/templates/html/contact-us.html
  27. 30 10
      src/main/resources/templates/html/modal/lg-advice-modal.html

+ 23 - 3
build.gradle

@@ -22,6 +22,7 @@ repositories {
     mavenCentral()
 }
 
+
 dependencies {
     implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
     implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
@@ -34,18 +35,37 @@ dependencies {
     testImplementation 'org.springframework.boot:spring-boot-starter-test'
     testImplementation 'org.springframework:spring-webflux'
     testImplementation 'org.springframework.graphql:spring-graphql-test'
-
     // log4j2
     implementation 'org.springframework.boot:spring-boot-starter-log4j2'
-
     //thymeleaf
     implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
     implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
-
     // mssql
     //implementation 'com.microsoft.sqlserver:mssql-jdbc:9.4.0.jre8'
     //mysql
     implementation 'mysql:mysql-connector-java:8.0.32'
+    //queryDSL
+    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
+    annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
+    annotationProcessor "jakarta.annotation:jakarta.annotation-api"
+    annotationProcessor "jakarta.persistence:jakarta.persistence-api"
+    // WebClient
+    implementation 'org.springframework.boot:spring-boot-starter-webflux'
+
+    /** Querydsl 설정부 **/
+    def generated = 'src/main/generated'
+//querydsl QClass 파일 생성 위치를 지정
+    tasks.withType(JavaCompile) {
+        options.getGeneratedSourceOutputDirectory().set(file(generated))
+    }
+//java source set 에 querydsl QClass 위치 추가
+    sourceSets {
+        main.java.srcDirs += [ generated ]
+    }
+//gradle clean 시에 QClass 디렉토리 삭제
+    clean {
+        delete file(generated)
+    }
 
 
 }

+ 76 - 1
src/main/java/com/example/ili_web/board/controller/BoardController.java

@@ -1,2 +1,77 @@
-package com.example.ili_web.board.controller;public class BoardController {
+package com.example.ili_web.board.controller;
+
+import com.example.ili_web.board.dto.*;
+import com.example.ili_web.board.service.BoardService;
+import com.example.ili_web.main.response.CheckDupleRes;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+@Controller
+@Slf4j
+@RequestMapping("/api/v1")
+@RequiredArgsConstructor
+public class BoardController {
+
+    private final BoardService boardService;
+    /**
+     method -> getBoardList
+     description -> 고객센터 알림/문의 목록
+     @author -> 김주한
+     @return -> boardList
+     date -> 2024.03.14
+     **/
+    @PostMapping("/board/list")
+    public ResponseEntity<BoardRes> getBoardList (@RequestBody BoardReq req) {
+        log.info("========== Board List API ==========");
+        log.info("type = " + req.getType());
+        BoardRes bList = boardService.getBoarList(req);
+        return ResponseEntity.ok(bList);
+    }
+
+    /**
+     method -> sendSms
+     description -> 인증문자 전송
+     @author -> 김주한
+     @return -> SuccessRes
+     date -> 2024.03.14
+     **/
+    @GetMapping("/send/sms")
+    public ResponseEntity<SuccessRes> sendSms (SendSmsReq req){
+        log.info("========== Send SMS API ==========");
+        SuccessRes res = boardService.sendSms(req);
+        return ResponseEntity.ok(res);
+    }
+
+    /**
+     method -> checkNickName
+     description -> 닉네임 중복확인
+     @author -> 김주한
+     @return -> CheckDupleRes
+     date -> 2024.03.14
+     **/
+    @PostMapping("/board/nickname")
+    public ResponseEntity<CheckDupleRes> checkNickName (@RequestBody BoardInfoReq req) {
+        log.info("========== Check Nick Name ==========");
+        return ResponseEntity.ok(boardService.checkNickName(req));
+    }
+
+    /**
+     method -> checkNickName
+     description -> 닉네임 중복확인
+     @author -> 김주한
+     @return -> CheckDupleRes
+     date -> 2024.03.14
+     **/
+    @PostMapping("/board/add")
+    public ResponseEntity insertBoard (@RequestBody BoardInfoReq req, @RequestParam("file") MultipartFile file) {
+        log.info("========== Insert Board Data ==========");
+        log.info("data = " + req.getTitle());
+        log.info("data = " + req.getUserName());
+        log.info("data = " + file.getContentType());
+        return null;
+    }
 }

+ 56 - 1
src/main/java/com/example/ili_web/board/domain/Board.java

@@ -1,2 +1,57 @@
-package com.example.ili_web.board.domain;public class Board {
+package com.example.ili_web.board.domain;
+
+import jakarta.persistence.*;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Entity
+@NoArgsConstructor
+@Table(name="BOARD")
+public class Board {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Column(nullable = false)
+    private String userName;
+    @Column(nullable = false)
+    private String password;
+    @Column
+    private String company;
+    @Column
+    private String phoneNum;
+    @Column
+    private String nickName;
+    @Column
+    private String title;
+    @Column
+    private String content;
+    @Column
+    private String type;
+    @Column
+    private String useYn;
+    @Column
+    private LocalDateTime regDt;
+    @Column
+    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;
+    }
 }

+ 13 - 1
src/main/java/com/example/ili_web/board/dto/AdviceList.java

@@ -1,2 +1,14 @@
-package com.example.ili_web.board.dto;public class AdviceList {
+package com.example.ili_web.board.dto;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class AdviceList {
+    private String userName;
+    private String nickName;
+    private String content;
+    private String type;
+    private LocalDateTime regDt;
 }

+ 18 - 0
src/main/java/com/example/ili_web/board/dto/BoardInfoReq.java

@@ -0,0 +1,18 @@
+package com.example.ili_web.board.dto;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class BoardInfoReq {
+    private String title;
+    private String userName;
+    private String content;
+    private String type;
+    private String email;
+    private String password;
+    private String phone;
+    private String company;
+    private String nickname;
+}

+ 14 - 1
src/main/java/com/example/ili_web/board/dto/BoardList.java

@@ -1,2 +1,15 @@
-package com.example.ili_web.board.dto;public class BoardList {
+package com.example.ili_web.board.dto;
+
+import lombok.Data;
+import lombok.Getter;
+
+import java.time.LocalDateTime;
+
+@Getter
+public class BoardList {
+    private String title;
+    private String userName;
+    private String content;
+    private String type;
+    private LocalDateTime regDt;
 }

+ 11 - 1
src/main/java/com/example/ili_web/board/dto/BoardReq.java

@@ -1,2 +1,12 @@
-package com.example.ili_web.board.dto;public class BoardReq {
+package com.example.ili_web.board.dto;
+
+import lombok.Data;
+
+@Data
+public class BoardReq {
+    private String userName;
+    private String type;
+    private int page;
+    private int offset;
+    private int limit;
 }

+ 12 - 1
src/main/java/com/example/ili_web/board/dto/BoardRes.java

@@ -1,2 +1,13 @@
-package com.example.ili_web.board.dto;public class BoardRes {
+package com.example.ili_web.board.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class BoardRes {
+    private List<?> dataList;
+    private int offset;
+    private int page;
+    private Long total;
 }

+ 9 - 0
src/main/java/com/example/ili_web/board/dto/SendSmsReq.java

@@ -0,0 +1,9 @@
+package com.example.ili_web.board.dto;
+
+import lombok.Data;
+
+@Data
+public class SendSmsReq {
+    private String randomCode;
+    private String phoneNum;
+}

+ 14 - 0
src/main/java/com/example/ili_web/board/dto/SuccessRes.java

@@ -0,0 +1,14 @@
+package com.example.ili_web.board.dto;
+
+import lombok.Data;
+
+@Data
+public class SuccessRes {
+    private String status;
+    private String msg;
+
+    public SuccessRes(String status, String msg) {
+        this.status = status;
+        this.msg = msg;
+    }
+}

+ 7 - 1
src/main/java/com/example/ili_web/board/repository/BoardRepository.java

@@ -1,2 +1,8 @@
-package com.example.ili_web.board.repository;public interface BoardRepository {
+package com.example.ili_web.board.repository;
+
+import com.example.ili_web.board.domain.Board;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface BoardRepository extends JpaRepository<Board, Long> {
+    long countBoardsByNickName(String nick_name);
 }

+ 34 - 7
src/main/java/com/example/ili_web/board/repository/BoardRepositoryDsl.java

@@ -1,10 +1,16 @@
 package com.example.ili_web.board.repository;
 
+import com.example.ili_web.board.domain.Board;
 import com.example.ili_web.board.domain.QBoard;
 import com.example.ili_web.board.dto.BoardList;
+import com.example.ili_web.board.dto.BoardReq;
+import com.example.ili_web.board.dto.BoardRes;
+import com.querydsl.core.BooleanBuilder;
+import com.querydsl.core.QueryResults;
 import com.querydsl.core.types.Projections;
+import com.querydsl.core.types.dsl.BooleanExpression;
+import com.querydsl.jpa.impl.JPAQuery;
 import com.querydsl.jpa.impl.JPAQueryFactory;
-import jakarta.persistence.EntityManager;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Repository;
 
@@ -12,16 +18,37 @@ import java.util.List;
 
 @RequiredArgsConstructor
 @Repository
-public class BoardRepositoryImpl {
+public class BoardRepositoryDsl {
     private final JPAQueryFactory queryFactory;
+    QBoard b = QBoard.board;
 
-    public List<BoardList> boardList() {
-        QBoard b = QBoard.board;
-        return queryFactory
-                .select(Projections.constructor(BoardList.class, b.title, b.userName, b.content, b.regDt))
+    public BoardRes boardList(BoardReq req) {
+        BoardRes res = new BoardRes();
+
+        List<BoardList> bList = queryFactory
+                .select(Projections.fields(BoardList.class, b.title, b.userName, b.content, b.type, b.regDt))
                 .from(b)
-                .where(b.type.eq("NT"))
+                .where(typeEq(req.getType()))
+                .offset(req.getOffset())
+                .limit(req.getLimit())
+                .orderBy(b.id.desc())
                 .fetch();
+
+        Long total = queryFactory
+                .select(b.count())
+                .from(b)
+                .where(typeEq(req.getType()))
+                .orderBy(b.id.desc())
+                .fetchOne();
+        res.setDataList(bList);
+        total = total != null && total > 10 ? total / 10 : 1;
+        res.setTotal(total);
+        res.setPage(req.getPage());
+        return res;
+    }
+
+    private BooleanExpression typeEq (String type) {
+        return type.equals("NT") ? b.type.in("NT", "GD") : b.type.eq(type);
     }
 
 }

+ 84 - 1
src/main/java/com/example/ili_web/board/service/BoardService.java

@@ -1,2 +1,85 @@
-package com.example.ili_web.board.service;public class BoardService {
+package com.example.ili_web.board.service;
+
+import com.example.ili_web.board.dto.*;
+import com.example.ili_web.board.repository.BoardRepository;
+import com.example.ili_web.board.repository.BoardRepositoryDsl;
+import com.example.ili_web.main.response.CheckDupleRes;
+import lombok.RequiredArgsConstructor;
+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.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 static java.net.http.HttpClient.*;
+
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class BoardService {
+    private final BoardRepository repository;
+    private final BoardRepositoryDsl boardDsl;
+    @Value("${global.message.sms.address}")
+    private String address;
+    @Value("${global.message.sms.url}")
+    private String smsUrl;
+    @Value("${global.message.sms.id}")
+    private String smsId;
+    @Value("${global.message.sms.pass}")
+    private String smsPw;
+
+    public BoardRes getBoarList(BoardReq req) {
+        int currentPage = req.getPage();
+        int pageSize = 10;
+        req.setOffset((currentPage - 1) * pageSize);
+        req.setLimit(pageSize);
+        if (req.getType().equals("NT")) {
+            return boardDsl.boardList(req);
+        } else {
+            return null;
+        }
+
+    }
+
+    public SuccessRes sendSms(SendSmsReq req) {
+        WebClient webClient = createWebClientWithRedirect(address);
+        String msg = "인증번호 [" + req.getRandomCode() + "]";
+
+        String result = webClient
+                .get()
+                .uri(uriBuilder -> uriBuilder
+                        .path(smsUrl)
+                        .queryParam("remote_id", smsId)
+                        .queryParam("remote_pass", smsPw)
+                        .queryParam("remote_num", 1)
+                        .queryParam("remote_phone", req.getPhoneNum())
+                        .queryParam("remote_callback", "0327728481")
+                        .queryParam("remote_msg", msg)
+                        .build())
+                .retrieve()
+                .bodyToMono(String.class)
+                .block();
+        if (result.equals("0000|전송 성공|48678|1")) {
+            return new SuccessRes("200", result);
+        } else {
+            return new SuccessRes("500", result);
+        }
+    }
+
+    public WebClient createWebClientWithRedirect(String address) {
+        HttpClient httpClient = HttpClient.create()
+                .followRedirect(true); // 리다이렉션을 자동으로 따르도록 설정
+
+        return WebClient.builder()
+                .baseUrl(address)
+                .clientConnector(new ReactorClientHttpConnector(httpClient))
+                .build();
+    }
+
+    public CheckDupleRes checkNickName(BoardInfoReq req) {
+        return new CheckDupleRes(repository.countBoardsByNickName(req.getNickname()));
+    }
 }

+ 16 - 1
src/main/java/com/example/ili_web/config/QueryDslConfig.java

@@ -1,2 +1,17 @@
-package com.example.ili_web.config;public class QueryDslConfig {
+package com.example.ili_web.config;
+
+import com.querydsl.jpa.impl.JPAQueryFactory;
+import jakarta.persistence.EntityManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class QueryDslConfig {
+
+    @Autowired private EntityManager em;
+    @Bean
+    public JPAQueryFactory jpaQueryFactory() {
+        return new JPAQueryFactory(em);
+    }
 }

+ 12 - 0
src/main/java/com/example/ili_web/main/response/CheckDupleRes.java

@@ -0,0 +1,12 @@
+package com.example.ili_web.main.response;
+
+import lombok.Data;
+
+@Data
+public class CheckDupleRes {
+    private long checkCount;
+
+    public CheckDupleRes(long checkCount) {
+        this.checkCount = checkCount;
+    }
+}

+ 12 - 3
src/main/resources/application.properties

@@ -1,13 +1,22 @@
-spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
+#spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
 logging.level.org.springframework.web=INFO
 #local
-spring.datasource.url=jdbc:mysql://103.55.191.37:3306/logisticsbiz?iliweb_test
+spring.datasource.url=jdbc:mysql://103.55.191.37:3306/iliweb_test
 #server
-#spring.datasource.url=jdbc:mysql://127.0.0.1:3306/logisticsbiz?iliweb_test
+#spring.datasource.url=jdbc:mysql://127.0.0.1:3306/iliweb
 spring.datasource.username=logisticsbiz
 spring.datasource.password=#il2020)(*
 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
 
+#JPA
+spring.jpa.database=mysql
+spring.jpa.show-sql=true
 
 #?? ??
 server.port=80
+
+#SMS
+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

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

@@ -1,4 +1,4 @@
-function openApply () {
+function openApply() {
     const manual = document.getElementById('advice_manual');
     const manualHead = document.getElementById('advice_manual_header');
     const apply = document.getElementById('advice_apply');
@@ -10,8 +10,35 @@ function openApply () {
     applyHead.style.display = 'block';
 }
 
-function subForm () {
-    if (validationCheck()) {
+async function dupleNickCheck() {
+    const nicknameInput = document.getElementById('lg_nickname');
+    const nickname = nicknameInput.value;
+    if (nickname.length > 0) {
+        const res = await axios.post('api/v1/board/nickname', {nickname})
+        if (res.data.checkCount > 0) {
+            alert('중복된 닉네임입니다.');
+            nicknameInput.value = null;
+        } else {
+            alert('사용 가능한 닉네임입니다.');
+            nicknameInput.disabled = true;
+        }
+    }
+}
 
+function subForm() {
+    if (validationCheck()) {
+        const form = document.getElementById('logi_ad_form')
+        const formData = new FormData(form);
+        formData.append('userName', document.getElementById('lg_name').value);
+        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'}})
+            .then(function (res) {
+                console.log(res)
+            })
+            .catch(function (e) {
+                console.log(e)
+            })
     }
 }

+ 52 - 4
src/main/resources/static/js/contact/adviceValidation.js

@@ -1,7 +1,55 @@
 function validationCheck () {
-    const form = document.getElementById('logi_ad_form')
-    alert(form.elements['name'].value);
-    if (form.elements['name'].value.length < 1 || form.elements['name'].value.length > 12) {
-        alert('이름이 너무 큽니다.')
+    const password = document.getElementById('lg_password');
+    const passwordConfirm = document.getElementById('lg_password_confirm');
+    const nickname = document.getElementById('lg_nickname');
+    const company = document.getElementById('lg_company');
+    const title = document.getElementById('lg_title');
+    const content = document.getElementById('lg_content');
+    if (password.value.length < 4) {
+        alert('비밀번호는 최소 4글자로 입력해주세요.');
+        password.focus();
+        return false;
     }
+    if (password.value !== passwordConfirm.value) {
+        alert('비밀번호가 일치하지 않습니다.');
+        passwordConfirm.focus();
+        return false;
+    }
+    if (nickname.value.length < 0) {
+        alert('닉네임을 입력해주세요.');
+        nickname.focus();
+        return false;
+    }
+    if (nickname.disabled !== true) {
+        alert('닉네임 중복확인을 해주세요.');
+        nickname.focus();
+        return false;
+    }
+    if (company.value > 40) {
+        alert('상호명은 40글자를 초과할 수 없습니다.');
+        company.focus();
+        return false;
+    }
+    if (title.value.length <= 0) {
+        alert('제목을 입력해주세요.');
+        title.focus();
+        return false;
+    }
+    if (title.value.length > 100) {
+        alert('제목은 100글자를 초과할 수 없습니다.');
+        title.focus();
+        return false;
+    }
+    if (content.value.length > 2000) {
+        alert('내용은 2000글자를 초과할 수 없습니다.');
+        content.focus();
+        return false;
+    }
+    if (content.value.length <= 0) {
+        alert('내용을 입력해주세요.');
+        content.focus();
+        return false;
+    }
+
+    return true;
 }

+ 85 - 0
src/main/resources/static/js/contact/auth-phone.js

@@ -0,0 +1,85 @@
+let intervalCount;
+
+// 인증번호 전송
+async function beforeAuth() {
+    const phoneNum = document.getElementById('check_phone').value
+    if (phoneNum.length === 0) {
+        alert('휴대폰 번호를 입력해 주세요.');
+        return false;
+    }
+    // 인증번호 생성
+    const randomCode = Math.floor(1000000 + Math.random() * 9000000);
+    // 문자전송 api 호출
+    await axios.get('/api/v1/send/sms', {
+        params: {
+            randomCode,
+            phoneNum
+        }
+    })
+    setCookie('authNum', randomCode, 3);
+    const timer = document.getElementById('timer');
+    timer.style.display = 'block';
+    countDown();
+}
+
+// 인증버튼 클릭
+function afterAuth() {
+    const phoneNumInput = document.getElementById('check_phone');
+    const authNumInput = document.getElementById('check_apply_num');
+    const phoneBtn = document.getElementById('button_phone');
+    const authBtn = document.getElementById('button_auth');
+    const timer = document.getElementById('timer');
+    const authVal = document.getElementById('check_apply_num').value;
+    if (authVal.length === 7) {
+        const authNum = getCookie('authNum');
+        if (authVal === authNum) {
+            alert('인증이 완료되었습니다.');
+            phoneNumInput.disabled = true;
+            authNumInput.disabled = true;
+            phoneBtn.disabled = true;
+            authBtn.disabled = true;
+            timer.style.display = 'none';
+            clearInterval(intervalCount);
+        } else {
+            alert('인증번호가 일치하지 않습니다.');
+        }
+    } else {
+        alert('인증번호를 확인하세요.');
+    }
+
+}
+
+//정보 인증 후 메인 페이지 전송
+function saveAuth() {
+    const name = document.getElementById('check_name').value;
+    const email = document.getElementById('check_email').value;
+    const phone = document.getElementById('check_phone').value;
+    if (name.length <= 0) {alert('이름을 입렵하세요.'); return false}
+    if (email.length <= 0) {alert('이메일을 입렵하세요.'); return false}
+    if (phone.length <= 0) {alert('휴대폰인증을 진행하세요.'); return false}
+    document.getElementById('lg_name').value = name;
+    document.getElementById('lg_email').value = email;
+    document.getElementById('lg_phone').value = phone;
+    document.getElementById('button-phone').disabled = true;
+    closeModal();
+}
+
+// 타이머 함수
+function countDown() {
+    let time = 180;
+    const timer = document.getElementById('timer');
+    const buttonAuth = document.getElementById('button_auth');
+    intervalCount = setInterval(function () {
+        if (time > 0) {
+            --time;
+            let min = Math.floor(time / 60);
+            let sec = String(time % 60).padStart(2, "0");
+            timer.innerText = `${min} : ${sec}`;
+        } else {
+            buttonAuth.disabled = true;
+            alert('인증시간이 만료되었습니다.');
+            clearInterval(intervalCount);
+        }
+    }, 1000);
+}
+

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

@@ -1,20 +1,6 @@
-/*function getList(type, page) {
-    axios.post('/api/v1/board/list', {type , page, size : 5})
-        .then(res => {
-            if (res.status === 200) {
-                makeTable(res.data.dataList)
-                pagenation(res.data);
-            } else {
-                console.log(res.statusText)
-            }
-        })
-        .catch(error => console.error('Error:', error));
-}*/
 async function getList(type, page) {
     try {
-        const response = await axios.post('/api/v1/board/list', {type , page, size : 5})
-        console.log(response);
-        return response;
+        return await axios.post('/api/v1/board/list', {type , page, size : 5});
     } catch (e) {
 
     }

+ 4 - 0
src/main/resources/static/js/contact/openModal.js

@@ -3,4 +3,8 @@ function openModal (tag) {
     modal.style.display = 'flex'
 }
 
+function closeModal () {
+    const modal = document.querySelector('.modal');
+    modal.style.display = 'none'
+}
 

+ 10 - 0
src/main/resources/static/js/cookie/cookie-control.js

@@ -0,0 +1,10 @@
+function setCookie (name, value, exp) {
+    let date = new Date();
+    date.setTime(date.getTime() + exp*60*1000);
+    document.cookie = `${name}=${value};expires=${date.toUTCString()};path=/`;
+}
+
+function getCookie (name) {
+    let value = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
+    return value? value[2] : null;
+}

+ 87 - 10
src/main/resources/templates/html/contact-inner/estimate.html

@@ -1,10 +1,87 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <title>$Title$</title>
-</head>
-<body>
-$END$
-</body>
-</html>
+<div class="row">
+    <div class="col-lg-4">
+    </div>
+    <div class="col-lg-4">
+        <h1>물류 견적</h1>
+    </div>
+    <div class="col-lg-4">
+    </div>
+</div>
+<!-- Grid -->
+<div class="row" style="height: 300px;">
+    <!-- 왼쪽 영역: 문의 목록 표시 -->
+    <div class="col-md-6">
+        <h3 class="text-center">물류견적목록</h3>
+        <table class="table table-striped">
+            <thead>
+            <tr>
+                <th>일시</th>
+                <th>제목</th>
+                <th>작성자</th>
+            </tr>
+            </thead>
+            <tbody>
+            <!-- 여기에 문의 목록을 동적으로 추가 -->
+            <tr>
+                <td>2024-03-04</td>
+                <td>문의 제목1</td>
+                <td>김주한</td>
+            </tr>
+            <tr>
+                <td>2024-03-04</td>
+                <td>문의 제목1</td>
+                <td>김주한</td>
+            </tr>
+            <tr>
+                <td>2024-03-04</td>
+                <td>문의 제목1</td>
+                <td>김주한</td>
+            </tr>
+            <tr>
+                <td>2024-03-04</td>
+                <td>문의 제목1</td>
+                <td>김주한</td>
+            </tr>
+            <tr>
+                <td>2024-03-04</td>
+                <td>문의 제목1</td>
+                <td>김주한</td>
+            </tr>
+            <!-- 추가적인 문의 목록 행들을 여기에 추가 -->
+            </tbody>
+        </table>
+        <nav aria-label="Page navigation">
+            <ul class="pagination justify-content-center">
+                <li class="page-item disabled">
+                    <a aria-disabled="true" class="page-link" href="#" tabindex="-1">Previous</a>
+                </li>
+                <li class="page-item"><a class="page-link" href="#">1</a></li>
+                <li class="page-item"><a class="page-link" href="#">2</a></li>
+                <li class="page-item"><a class="page-link" href="#">3</a></li>
+                <li class="page-item">
+                    <a class="page-link" href="#">Next</a>
+                </li>
+            </ul>
+        </nav>
+    </div>
+    <!-- 오른쪽 영역: 문의 내용 표시 -->
+    <div class="col-md-6">
+        <h3 class="text-center">물류견적메뉴얼</h3>
+        <div class="row">
+
+            <div class="col">
+                <h5 class="text-center"> 물류견적이 필요하신가요?
+                    그럼 위 접수증을 열어 물류견적을 신청해 보세요
+                    당사의 각지사를 통하여 귀하에게 꼭 맞는 최적의 견적서를 받아  보실수 있습니다
+                </h5>
+            </div>
+            <!-- 내용 표시 textarea -->
+            <div class="row">
+                <!-- 내용 표시 textarea -->
+                <div class="col">
+                    <textarea class="form-control" rows="5" readonly>문의 내용을 여기에 표시합니다.</textarea>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 86 - 53
src/main/resources/templates/html/contact-inner/logi-advice.html

@@ -5,10 +5,11 @@
         <h1>물류 자문</h1>
     </div>
     <div class="col-lg-4">
+        <button class="btn btn-dark" onclick="openApply()" type="button">물류자문신청서</button>
     </div>
 </div>
 <!-- Grid -->
-<div class="row" style="height: 300px;">
+<div class="row" style="display: flex">
     <!-- 왼쪽 영역: 문의 목록 표시 -->
     <div class="col-md-6">
         <h3 class="text-center">문의 목록</h3>
@@ -16,70 +17,102 @@
             <thead>
             <tr>
                 <th>일시</th>
-                <th>제목</th>
-                <th>작성자</th>
+                <th>이름</th>
+                <th>닉네임</th>
             </tr>
             </thead>
             <tbody>
-            <!-- 여기에 문의 목록을 동적으로 추가 -->
-            <tr>
-                <td>2024-03-04</td>
-                <td>문의 제목1</td>
-                <td>김주한</td>
-            </tr>
-            <tr>
-                <td>2024-03-04</td>
-                <td>문의 제목1</td>
-                <td>김주한</td>
-            </tr>
-            <tr>
-                <td>2024-03-04</td>
-                <td>문의 제목1</td>
-                <td>김주한</td>
-            </tr>
-            <tr>
-                <td>2024-03-04</td>
-                <td>문의 제목1</td>
-                <td>김주한</td>
-            </tr>
-            <tr>
-                <td>2024-03-04</td>
-                <td>문의 제목1</td>
-                <td>김주한</td>
-            </tr>
             <!-- 추가적인 문의 목록 행들을 여기에 추가 -->
             </tbody>
         </table>
         <nav aria-label="Page navigation">
-            <ul class="pagination justify-content-center">
-                <li class="page-item disabled">
-                    <a aria-disabled="true" class="page-link" href="#" tabindex="-1">Previous</a>
-                </li>
-                <li class="page-item"><a class="page-link" href="#">1</a></li>
-                <li class="page-item"><a class="page-link" href="#">2</a></li>
-                <li class="page-item"><a class="page-link" href="#">3</a></li>
-                <li class="page-item">
-                    <a class="page-link" href="#">Next</a>
-                </li>
-            </ul>
+
         </nav>
     </div>
     <!-- 오른쪽 영역: 문의 내용 표시 -->
     <div class="col-md-6">
-        <h3 class="text-center">문의 내용</h3>
-        <div class="row">
-
-            <div class="col">
-                <!-- 일시, 제목, 작성자 표시 -->
-                <p><strong>일시:</strong> 2024-03-04 15:00</p>
-                <p><strong>제목:</strong> 문의 제목 1</p>
-                <p><strong>작성자:</strong> 작성자 이름</p>
+        <h3 class="text-center" id="advice_manual_header">물류자문 메뉴얼</h3>
+        <h3 class="text-center" id="advice_apply_header" style="display: none">물류자문 신청서</h3>
+        <div class="row" id="advice_apply" style="display: none">
+            <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>
+                            <input class="form-control" id="lg_name" required type="text" name="userName" disabled>
+                        </div>
+                        <div class="col-md-6">
+                            <label class="form-label" for="lg_phone">핸드폰번호</label>
+                            <div class="input-group mb-3">
+                                <input class="form-control" id="lg_phone" required type="tel" name="phoneNum" disabled>
+                                <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>
+                            <input class="form-control" id="lg_email" required type="email" name="email" disabled>
+                        </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">
+                                <input class="form-control" id="lg_nickname" name="nickname" required type="text">
+                                <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>
+                            <input class="form-control" id="lg_company" name="company" type="text">
+                        </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">
+                        </div>
+                    </div>
+                    <div class="mb-3">
+                        <label class="form-label" for="lg_title">제목</label>
+                        <input class="form-control" id="lg_title" required name="title" type="text">
+                    </div>
+                    <div class="mb-3">
+                        <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>
             </div>
-            <!-- 내용 표시 textarea -->
-            <div class="row">
-                <!-- 내용 표시 textarea -->
-                <div class="col">
-                    <textarea class="form-control" rows="5" readonly>문의 내용을 여기에 표시합니다.</textarea>
+        </div>
+        <div class="row" id="advice_manual">
+
+            <div class="col d-flex flex-column flex-md-row p-4 gap-4 py-md-5 align-items-center justify-content-center">
+                <div class="list-group">
+                    <div class="d-flex gap-2 w-100 justify-content-between">
+                        <div>
+                            <h6 class="mb-0 text-center">물류자문이 필요하신가요?</h6>
+                            <br>
+                            <p class="mb-0 opacity-75 text-center">그렇다면 위 신청서를 열어 물류자문을 신청해 보세요.</p>
+                            <p class="mb-0 opacity-75 text-center">당사의 전세계 50여명의 전문가가 귀하에게 최적의 방안을 제시해 드립니다.</p>
+                        </div>
+                    </div>
+                    <hr class="featurette-divider">
+                    <div class="d-flex gap-2 w-100 justify-content-between">
+                        <div>
+                            <h6 class="mb-0">**물류자문접수**</h6>
+                            <br>
+                            <p class="mb-0 opacity-75">1,위 물류자문신청서를 선택합니다.</p>
+                            <p class="mb-0 opacity-75">2,신청서에 자세한 상황을 기록합니다.</p>
+                            <p class="mb-0 opacity-75"> 2-1) 정확한 자문을 위해서는 상세하고도 사실적인 내용을 입력해야 합니다.</p>
+                            <p class="mb-0 opacity-75"> 2-2) 문서, 사진, 인터넷 사이트 등 참고자료도 제시합니다.</p>
+                            <p class="mb-0 opacity-75">3, 간단한 인증을 받고 신청서를 접수합니다.</p>
+                        </div>
+                    </div>
                 </div>
             </div>
         </div>

+ 10 - 36
src/main/resources/templates/html/contact-inner/notice.html

@@ -12,7 +12,7 @@
     <!-- 왼쪽 영역: 문의 목록 표시 -->
     <div class="col-md-6">
         <h3 class="text-center">공지 목록</h3>
-        <table class="table table-striped">
+        <table class="table table-striped" id="notice_table">
             <thead>
             <tr>
                 <th>일시</th>
@@ -21,37 +21,11 @@
             </tr>
             </thead>
             <tbody>
-            <!-- 여기에 문의 목록을 동적으로 추가 -->
-            <tr>
-                <td>2024-03-04</td>
-                <td>문의 제목1</td>
-                <td>김주한</td>
-            </tr>
-            <tr>
-                <td>2024-03-04</td>
-                <td>문의 제목1</td>
-                <td>김주한</td>
-            </tr>
-            <tr>
-                <td>2024-03-04</td>
-                <td>문의 제목1</td>
-                <td>김주한</td>
-            </tr>
-            <tr>
-                <td>2024-03-04</td>
-                <td>문의 제목1</td>
-                <td>김주한</td>
-            </tr>
-            <tr>
-                <td>2024-03-04</td>
-                <td>문의 제목1</td>
-                <td>김주한</td>
-            </tr>
-            <!-- 추가적인 문의 목록 행들을 여기에 추가 -->
+
             </tbody>
         </table>
-        <nav aria-label="Page navigation">
-            <ul class="pagination justify-content-center">
+        <nav aria-label="Page navigation" id="pageTag">
+           <!-- <ul class="pagination justify-content-center">
                 <li class="page-item disabled">
                     <a aria-disabled="true" class="page-link" href="#" tabindex="-1">Previous</a>
                 </li>
@@ -61,25 +35,25 @@
                 <li class="page-item">
                     <a class="page-link" href="#">Next</a>
                 </li>
-            </ul>
+            </ul>-->
         </nav>
     </div>
     <!-- 오른쪽 영역: 문의 내용 표시 -->
     <div class="col-md-6">
-        <h3 class="text-center">문의 내용</h3>
+        <h3 class="text-center">내용</h3>
         <div class="row">
 
             <div class="col">
                 <!-- 일시, 제목, 작성자 표시 -->
-                <p><strong>일시:</strong> 2024-03-04 15:00</p>
-                <p><strong>제목:</strong> 문의 제목 1</p>
-                <p><strong>작성자:</strong> 작성자 이름</p>
+                <p id="nt_dt"></p>
+                <p id="nt_title"></p>
+                <p id="nt_name"></p>
             </div>
             <!-- 내용 표시 textarea -->
             <div class="row">
                 <!-- 내용 표시 textarea -->
                 <div class="col">
-                    <textarea class="form-control" rows="5" readonly>문의 내용을 여기에 표시합니다.</textarea>
+                    <textarea id="nt_content" class="form-control" rows="5" readonly placeholder="문의 내용을 여기에 표시합니다."></textarea>
                 </div>
             </div>
         </div>

+ 18 - 11
src/main/resources/templates/html/contact-us.html

@@ -12,22 +12,18 @@
     <!-- Bootstrap core CSS -->
     <link href="/css/bootstrap.min.css" rel="stylesheet">
     <!-- swiper CSS-->
-    <!--    <link href="https://unpkg.com/swiper/swiper-bundle.min.css" rel="stylesheet">-->
 
 
     <!-- Custom styles for this template -->
     <link href="/css/index.css" rel="stylesheet">
+    <link href="/css/modal.css" rel="stylesheet">
 </head>
 <body>
+<div th:include="html/modal/lg-advice-modal"></div>
 <header th:include="html/common/header">
 </header>
 <main>
     <div class="carousel slide" data-bs-ride="carousel" id="myCarousel">
-        <!--<div class="carousel-indicators">
-            <button aria-label="Slide 1" data-bs-slide-to="0" data-bs-target="#myCarousel" type="button"></button>
-            <button aria-label="Slide 2" data-bs-slide-to="1" data-bs-target="#myCarousel" type="button"></button>
-            <button aria-label="Slide 3" data-bs-slide-to="2" data-bs-target="#myCarousel" type="button"></button>
-        </div>-->
         <div class="carousel-inner">
             <div class="carousel-item active">
                 <svg aria-hidden="true" class="bd-placeholder-img" focusable="false" height="100%"
@@ -39,10 +35,10 @@
                     <div class="carousel-caption">
                         <h1>고객센터</h1>
                         <p>The Master of Global Logistics</p>
-                        <p><a class="btn" href="#plane" th:onclick="scrollToDiv('outline')">안내/공지</a>
-                            <a class="btn" href="#greeting" th:onclick="scrollToDiv('greeting')">물류견적</a>
-                            <a class="btn" href="#greeting" th:onclick="scrollToDiv('greeting')">물류자문</a>
-                            <a class="btn" href="#history" th:onclick="scrollToDiv('history')">무역자문</a>
+                        <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>
+                            <a class="btn" href="javascript:void(0);" th:onclick="changeInner('history')">무역자문</a>
                         </p>
                     </div>
                 </div>
@@ -53,7 +49,9 @@
 
     <div class="container marketing" id="notice" th:include="html/contact-inner/notice">
     </div>
-    <div class="container marketing" id="logi_advice" th:include="html/contact-inner/logi-advice">
+    <div class="container marketing" id="estimate" th:include="html/contact-inner/estimate" style="display: none">
+    </div>
+    <div class="container marketing" id="logi_advice" th:include="html/contact-inner/logi-advice" style="display: none">
     </div>
     <hr class="featurette-divider">
 </main>
@@ -62,4 +60,13 @@
 
 </footer>
 </body>
+<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
+<script src="/js/business-sector/bizChangeInner.js"></script>
+<script src="/js/contact/getList.js"></script>
+<script src="/js/contact/makeHtml.js"></script>
+<script src="/js/contact/advice.js"></script>
+<script src="/js/contact/adviceValidation.js"></script>
+<script src="/js/contact/openModal.js"></script>
+<script src="/js/cookie/cookie-control.js"></script>
+<script src="/js/contact/auth-phone.js"></script>
 </html>

+ 30 - 10
src/main/resources/templates/html/modal/lg-advice-modal.html

@@ -1,10 +1,30 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <title>$Title$</title>
-</head>
-<body>
-$END$
-</body>
-</html>
+<div class="modal">
+    <div class="modal_body">
+        <h2>개인정보 인증</h2>
+        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="closeModal()"></button>
+        <form id="check_info">
+            <div class="form-floating mb-3">
+                <input type="text" class="form-control" id="check_name" placeholder="홍깅동">
+                <label for="check_name">이름</label>
+            </div>
+            <div class="form-floating mb-3">
+                <input type="email" class="form-control" id="check_email" placeholder="name@example.com">
+                <label for="check_email">이메일</label>
+            </div>
+            <div class="form-floating mb-3 input-group">
+                <input type="tel" class="form-control" id="check_phone" placeholder="xxx-xxxx-xxxx">
+                <label for="check_phone">휴대폰번호</label>
+                <button class="btn btn-outline-secondary" id="button_phone" type="button" onclick="beforeAuth()">번호 발급</button>
+            </div>
+            <div class="form-floating mb-3 input-group">
+                <input type="email" class="form-control" id="check_apply_num" placeholder="0000000">
+                <label for="check_apply_num">인증번호</label>
+                <button class="btn btn-outline-secondary" id="button_auth" type="button" onclick="afterAuth()">인증 확인</button>
+            </div>
+            <p id="timer" style="display: none"></p>
+            <div class="form-floating mb-3">
+                <button type="button" class="btn btn-success" onclick="saveAuth()">정보저장</button>
+            </div>
+        </form>
+    </div>
+</div>