27_공동구매 게시물 작성폼 ①
드디어 공동구매 게시물 작성폼...!!!
공동구매글을 작성하는 방법은 헤더에 있는 『작성』 아이콘을 클릭하면 된다. [아래 사진 참고]
로그인하지 않고, 클릭하면 앞에서와 마찬가지로 안내 팝업과 함께 로그인폼으로 이동시킨다.
로그인하고 아이콘을 클릭하면, 공동구매 게시물 작성폼으로 이동한다.
공동구매 게시물 작성폼의 필수/선택 입력 사항이다.
▶ 필수 입력 사항: 대표 사진 선택, 대분류/소분류 선택, 제목, 상품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;
}
나머지 작업은 다음 게시물에서 이어서,,,,!
