PROJECT/같이사자(공동구매)

27_공동구매 게시물 작성폼 ①

yong_ღ'ᴗ'ღ 2022. 9. 21. 04:53

드디어 공동구매 게시물 작성폼...!!!

공동구매글을 작성하는 방법은 헤더에 있는 『작성』 아이콘을 클릭하면 된다. [아래 사진 참고]

 

로그인하지 않고, 클릭하면 앞에서와 마찬가지로 안내 팝업과 함께 로그인폼으로 이동시킨다.

 

로그인하고 아이콘을 클릭하면, 공동구매 게시물 작성폼으로 이동한다.

공동구매 게시물 작성폼의 필수/선택 입력 사항이다.

▶ 필수 입력 사항: 대표 사진 선택, 대분류/소분류 선택, 제목, 상품URL, 유통기한입력(모르면 '알 수 없음'에 체크), 

                              상품의 총 금액 + 배송비, 모집상품개수, 모집마감날짜/시간, 거래희망날짜/시간, 거래위치, 

                              본인이 구매할 상품 개수

 

▶ 선택 입력 사항: 상세설명, 상세설명 사진 첨부

 


본격적으로 시작하기 전에, 공동구매 게시물 작성폼에서 해야 할 작업을 먼저 정리!

- 대분류 목록 가져오기 + 대분류 선택에 따른 소분류 목록 ajax 사용해서 가져오기

- 유통기한(datepicker): 현재보다 이전 날짜 선택 불가 처리, 날짜선택과 알 수 없음 모두 둘 중 하나만 입력 가능하게

- 상품의 총 금액+배송비 입력칸, 모집상품개수 입력칸에 숫자만 입력가능하도록

- 상품의 총 금액+배송비 입력칸, 모집상품개수 입력칸 모두 입력하고 <계산> 버튼 누르면 1인 가격 확인 가능하게

- 모집마감날짜(datepicker) + 시간(timepicker): 현재보다 이전 날짜는 선택 불가 & 현재로부터 21일 이내(3주)의

                                                                           날짜만 선택 가능, 시간은 30분 단위로 선택 가능

- 거래희망날짜(datepicker) + 시간(timepicker): 모집마감날짜 이후부터 14일 이내의 날짜만 선택 가능, 

                                                                           시간은 30분 단위로 선택 가능

                                                                           ★ 모집마감일 선택 후에, 거래희망일 선택 가능하게 처리!

- 거래위치선택: 지도API에서 거래장소 클릭하면 주소 표시해주기

게시물 다 작성하고 <등록> 버튼 누르면, 

- 필수 입력 사항 입력됐는지 체크 + 제대로 입력 안 됐으면 빨간 테두리+alert(), focus() 처리

- 결제 팝업: 참여자와 진행자 구분해서 팝업 창에서 보여줄 멘트 다르게 하려고 계획했었으니 그거 작업

- 결제 진행 후(결제 팝업) , 글 업로드(DB INSERT)


고럼 작업 시작!

 

1) 대표 사진

대표 사진은 선택 시, 이미지를 미리 볼 수 있도록 했다.

// script
// 사진 미리보기
$(function() {
    $("#buypostInsertImg-btn").on('change', function(){
    readURL(this);
    });
});
function readURL(input) {
    if (input.files && input.files[0]) {
        var reader = new FileReader();
        reader.onload = function (e) {
            $('#buypostInsert-previewImg').attr('src', e.target.result);
        }
        reader.readAsDataURL(input.files[0]);
    }
}
<!-- html -->
<div class="buypost-form-img">
    <img style="width: auto;" id="buypostInsert-previewImg">
</div>
<label class="file-btn" for="buypostInsertImg-btn">
    <div class="buypostInsertFileBtn-box">
        <i class="bi bi-folder-plus"></i>
        대표 사진을 선택해주세요 Click!
        <hr />
    </div>	
</label> 
<input type="file" id="buypostInsertImg-btn" style="display: none;"/>

 

 

2) 대분류 목록 가져오기 + 대분류 선택에 따른 소분류 목록 ajax 사용해서 가져오기

ajax부분에서 가져오는 코드를 짧게 적을 수 있지만, 현재 css부분에 nice-select를 사용해서 길어졌다.

nice-select를 쓰면서도 뭔가 짧게 쓰는 방법이 있을거는 같기는 하지만...ㅎ...!.....

<!-- 공동구매 게시물 view.jsp 파일 -->
<div class="product__category buypost__category">
    <div class="mainCategory">
        <select class="form-select mainCategory-select"
            aria-label="Default select example" name="main_cate_name"
            id="main_cate_select">
            <option value="0" selected>대분류 선택</option>
            <c:forEach var="mainCate" items="${mainCateList }">
                <option value="${mainCate.code }">${mainCate.name }</option>
            </c:forEach>
        </select>
    </div>
    <div class="subCategory">
        <select class="form-select subCategory-select"
            aria-label="Default select example" name="sub_cate_code">
            <option value="0" selected>소분류 선택</option>
        </select>
    </div>
</div>
// script 코드
// 대분류 선택에 따른 소분류 가져오기
$('#main_cate_select').change(function()
{
    if ($('#main_cate_select').val() != 0)
    {
        $.ajax({
            type: "POST"
            , url: "buypostsubcate.lion"
            , data: { code : $("#main_cate_select").val() }
            , success: function(result)
            {
                $('.subCategory').html(result);
            }
            , error: function(e)
            {
                alert(e.status);
                alert(e.responseText);
            }
        });
    }
// Controller
// 대분류에 따른 소분류 목록 가져오기
@RequestMapping("/buypostsubcate.lion")
public String buypostSubCate(HttpServletRequest request, Model model)
{
    String ajaxCode = "buypostSubcate";
    String main_cate_code = request.getParameter("code");
    IBuypostDAO dao = sqlSession.getMapper(IBuypostDAO.class);
    ArrayList<SubCategoryDTO> subCateList = dao.subCateList(main_cate_code);

    model.addAttribute("ajaxCode", ajaxCode);
    model.addAttribute("subCateList", subCateList);
    return "/WEB-INF/view/user_buypostInsertForm_ajax.jsp";
}
<!-- ajax관련 .jsp파일 -->

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<select class="form-select subCategory-select"
aria-label="Default select example" name="sub_cate_code" id="subCateSelect">
	<option value="0" selected>소분류 선택</option>
	<c:forEach var="subCate" items="${subCateList }">
		<option value="${subCate.code }">${subCate.name }</option>
	</c:forEach>
</select> 
<div class="nice-select form-select subCategory-select" tabindex="0">
	<span class="current">소분류 선택</span>
	<ul class="list">
		<li data-value="0" class="option focus">소분류 선택</li>
		<c:forEach var="subCate" items="${subCateList }">
			<li data-value="${subCate.code }" class="option">${subCate.name }</li>
		</c:forEach>
	</ul>
</div>

 

 

이렇게만 처리하고 화면을 보면, 목록은 가져와졌는데,

기본 selectbox와 nice selectbox 2개가 다 보여지기 때문에 css로 보이지 않도록 처리!

select#subCateSelect {
    display: none;
}

 

css까지 마친 ajax 처리로 소분류 목록을 가져온 결과 화면이다.

 

🔥 ajax 작성하면서 http 상태 코드는 200으로 잘 넘어갔는데 소분류 목록이 뜨지 않아서 한참 헤맸다...

아래는 원래 작성했던 코드이다.

$.ajax({
    type: "POST"
    , url: "buypostsubcate.lion"
    , data: { code : $("#main_cate_select").val() }
    , dataType: "json"
    , success: function(result)
    {
        $('.subCategory').html(result);
    }
    , error: function(e)
    {
        alert(e.status);
        alert(e.responseText);
    }
});

그런데 아래와 같이 작성하니까 소분류 목록이 떴다.

$.post("buypostsubcate.lion", { code : $('#main_cate_select').val() }, function(result)
{
    $('.subCategory').html(result);
});

 

 

두 코드에 차이가 없는 거 같은데 아래 코드로는 소분류 목록이 떠서 구글에도 검색해보고 했는데...

찾았다...!......ㅠ.ㅠ

위의 코드에서 dataType: "json" 이 부분 때문에 목록이 안 떴던 것이다.

이번 프로젝트에서 사용했던 회원가입 등 ajax 부분에서는 다 작성했어서 이번에도 그냥 작성했던게 문제..ㅎㅎ

『dataType: "json"』 는 옵션이므로 JSON으로 받을게 아니면 안써도 된다.

이 코드를 제거하면 ajax 호출은 json 반환 데이터 유형을 기대하지 않는다.

 

 

3) 유통기한(datepicker): 현재보다 이전 날짜 선택 불가 처리, 날짜선택과 알 수 없음 모두 둘 중 하나만 입력 가능하게

  +) datepicker setting

// DatePicker
$.datepicker.setDefaults({
      dateFormat: 'yy-mm-dd',
      prevText: '이전 달',
      nextText: '다음 달',
      monthNames: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
      monthNamesShort: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
      dayNames: ['일', '월', '화', '수', '목', '금', '토'],
      dayNamesShort: ['일', '월', '화', '수', '목', '금', '토'],
      dayNamesMin: ['일', '월', '화', '수', '목', '금', '토'],
      showMonthAfterYear: true,
      yearSuffix: '년'
});

// datepicker
$(function()
{
    $(".testDatepicker").datepicker({});
    //-- datepicker 쓰는 input의 class 속성에 모두 "testDatepicker" 적어줌
});

 

여기까지 하고, '유통기한을 선택해주세요' 텍스트 박스를 클릭하면, 달력이 예쁘게 잘 뜬다.

달력을 통한 날짜만 선택가능하게 하려고 readonly 속성을 추가해줬다.

<input type="text" id="expiration_date" name="expiration_datetime"
       class="testDatepicker buypost-text buypost-expiration-date"
       placeholder="유통기한을 선택해주세요." readonly/>

유통기한 부분에서 처리한 작업들이다.

  ① 날짜 선택: 현재포함 그 이후로만 가능하도록

  ② 날짜 선택한 후에, '알 수 없음'에 체크하면 → 날짜 선택했던 거 값 초기화

  ③ '알 수 없음'에 체크한 후에, 날짜 선택하면 → '알 수 없음' 체크박스의 체크 해제

// 유통기한
$("#expiration_date").datepicker({
    minDate: 0,
    onClose: function(selectedDate)
    {
        // '알 수 없음'에 체크했는데, 날짜 선택하면, '알 수 없음' 체크박스의 체크 해제
        if (selectedDate != "" && $("#expiration_date_checkbox").prop('checked') == true) 
            $("#expiration_date_checkbox").prop('checked', false);
    }
});
// 유통기한 체크박스
$('#expiration_date_checkbox').change(function()
{
    // 날짜 선택했는데, '알 수 없음'에 체크하면, 날짜 선택했던거 초기화
    if ($('#expiration_date_checkbox').is(':checked') && $("#expiration_date").val() != '') 
        $("#expiration_date").datepicker('setDate', "");
});

 

 

 

4) 상품의 총 금액+배송비 입력칸, 모집상품개수 입력칸에 숫자만 입력가능하도록

회원가입 파트에서 핸드폰 번호 입력받을 때에도 숫자만 입력가능하도록 했었다.

그때는 정규식을 사용했었는데, 이번에는 <input>의 type 속성 중 number를 사용해서 했다.

<div class="buypostForm-text">
    <input type="number" id="total_price" name="total_price" 
    class="buypost-text" required placeholder="상품의 총 금액 + 배송비를 입력해주세요." />
</div>

<div class="buypostForm-text">
    <input type="number" id="goods_num" name="goods_num" 
    class="buypost-text" required
        placeholder="모집상품개수 입력 (진행자 구매 수량 포함)" />
</div>

<input type="number">를 사용하면 아래 사진과 같이 input 텍스트박스에 화살표가 생기고,

화살표 위 아래를 누르면 사용자 입력 숫자에서 1씩 증가/감소한다.

 

 

5) 상품의 총 금액+배송비 입력칸, 모집상품개수 입력칸 모두 입력하고 <계산> 버튼 누르면 1인 가격 확인 가능하게 

// 1인 가격 계산
$('.calculateBtn').click(function()
{	
    let eachPrice = Number($('input#total_price').val()) / Number($('input#goods_num').val());
    $('span.price').text(Math.ceil(eachPrice));
});

현재 금액에는 72000원, 총 모집 상품개수는 10개를 입력해서 1인당 가격은 72000/10 = 7200원으로 계산되어 보여진다.

 

 

6) 모집마감날짜(datepicker) + 시간(timepicker): 현재보다 이전 날짜는 선택 불가

                                                                                & 현재로부터 21일 이내(3주)의 날짜만 선택 가능,

                                                                                시간은 30분 단위로 선택 가

모집마감날짜는 너무 길게 잡으면 안되기 때문에 글 등록일로부터 +21일 안에서만 선택 가능하도록 했다.

// 모집마감일 (현재 ~ +21일)
$("#deadline").datepicker({
    minDate: 0,
    maxDate: "+21D"
});

현재 9월도 다 끝나가서,,,,,,,,뒤에 10월 달력까지 넘겨보면

오늘(9/21) 이전은 선택 불가하고, 10/12 이후로는 선택 불가한 것을 확인할 수 있다.

시간선택은 timepicker를 사용했다.

  +) timepicker setting

interval을 30으로 설정해서 30분 단위로 선택가능하게 설정함

$('.timepicker').timepicker({
    timeFormat: 'HH:mm',
    interval: 30,
    minTime: '0',
    maxTime: '11:00pm',
    defaultTime: false,
    startTime: '9:00',
    dynamic: false,
    dropdown: true,
    scrollbar: true
});

$(function()
{
     $('input.timepicker').timepicker({});
     //-- timepicker 쓰는 input의 class 속성에 모두 "timepicker" 적어줌
})
<!-- 모집마감시간 선택 HTML 코드 -->
<input type="text" id="deadlineTime" class="timepicker" placeholder="시간 선택" readonly/>

 

오전 9시부터 해서 30분 간격으로 쭉- 나와있다.

모집마감 날짜와 시간을 모두 선택한 화면이다.

 

 

7) 거래희망날짜(datepicker) + 시간(timepicker): 모집마감날짜 이후부터 14일 이내의 날짜만 선택 가능, 

                                                                                시간은 30분 단위로 선택 가능

                                                                               ★ 모집마감일 선택 후, 거래희망일 선택 가능하게 처리!!

거래희망일은 모집마감일로부터 14일 이내로만 선택가능하게 정책적으로 정해놨다.

🔥 datepicker에서 모집마감일~+14일 이내로만 선택하게 하는 부분이 찾기 좀 힘들었다....... ㅠ,ㅠ.......

인터넷에 쳐도 뭔가 원하는 걸 얻기가 힘들ㅇ...ㅓㅆ다....아님 이해하기 어렵거나.....

year, month, day 객체를 원하는대로 만들어서 datepicker의 minDate와 maxDate 속성을 설정해서 성공! ㅎㅅㅎ~!

// 모집마감일 (현재 ~ +21일)
$("#deadline").datepicker({
    minDate: 0,
    maxDate: "+21D",
    onClose: function(selectedDate)
    {
        // 거래희망일 (모집마감일 ~ +14일)
        let year = new Date(selectedDate).getFullYear();
        let month = new Date(selectedDate).getMonth();
        let day = new Date(selectedDate).getDate()+14;

        $("#trade_date").datepicker("option", "minDate", selectedDate);
        $("#trade_date").datepicker("option", "maxDate", new Date(year, month, day));
    }
});

 

 

모집마감일로 10월 4일을 선택했고, 그에 따라 거래희망일은 10/4 ~ 10/18일까지만 선택가능하다.

그래서 모집마감일을 선택한 후에만!! 거래희망일을 선택할 수 있도록 처리를 해야할 줄 알았는데, 테스트해보니까

거래희망일을 모집마감일보다 이전으로 선택한 후에, 모집마감일을 추후에 선택하게 되면,

거래희망일이 모집마감일과 동일하게 알아서 처리가 된다~!

 

 

거래희망시간 부분은 위에서 바로 한 모집마감시간과 아예 동일하니 코드와 사진은 생략! ㅎㅅㅎ

 

 

8) 거래위치선택: 지도API에서 거래장소 클릭하면 주소 표시해주기

지도 API는 카카오지도를 사용했다. 메인 지도에서 한 것과 크게 다를 거 없었다.

거래 위치를 선택하려면 아래 사진에서 표시해놓은 [거래위치를 선택해주세요]라고 적힌 텍스트박스를 클릭하면 된다.

 

클릭하면, 아래와 같은 지도 팝업이 뜬다.

 

지도를 움직이며 위치를 클릭하면, 아래 사진과 같이 주소가 뜬다.

 

희망하는 거래 위치를 선택하고, <위치 선택 완료>를 누르면, 지도 팝업이 닫히고 작성폼에도 해당 주소가 적힌다.

지도 팝업이 닫힐 때, 자신을 호출한 부모창에 값을 전달하도록 했다.

<!-- 지도 팝업 HTML 코드 -->
<input type="text" id="detailAddr" readonly/>
<button type="button" class="btn btn-primary lion-primary-btn locationBtn"
onclick="javascript:passMapInfo();window.close()">위치 선택 완료</button>
// javascript 코드
function passMapInfo()
{
	opener.document.getElementById("location").value = document.getElementById("detailAddr").value;
	opener.document.getElementById("location-x").value = document.getElementById("lat").value;
	opener.document.getElementById("location-y").value = document.getElementById("lng").value;
	opener.document.getElementById("region").value = document.getElementById("region").value;
}

 

 


나머지 작업은 다음 게시물에서 이어서,,,,!