21.04.03~04.06 lamplight서비스 프로젝트(ionic이사 진행중-의뢰인로그인,회원가입,정보수정.....지도사리스팅....요청서crud, 리스팅 완료)
2021. 4. 6. 23:39ㆍVue.js/Spring & Vue APP 프로젝트(프론트엔드)
# NOTE
Todo
-ionic으로 일단 이사[]
-의뢰인로그인,로그아웃[ㅇ]
-의뢰인회원가입후 로그인아이디[ㅇ]
-의뢰인마이페이지[ㅇ]
-의뢰인회원정보 수정[ㅇ]
-지도사 리스팅[ㅇ]
-요청서 작성[ㅇ]/상세보기[ㅇ]
-요청서 수정[ㅇ]
-요청서 삭제[ㅇ]
-컨펌창 도입[ㅇ]
-요청서 작성시[ㅇ]
-요청서 수정시[ㅇ]
-회원가입시[ㅇ]
-회원정보 수정시[ㅇ]
-요청서 리스팅[ㅇ]
-ionic 컨펌창 도입[ㅇ]
https://forum.ionicframework.com/t/return-value-from-alert-confirmation/75824/2
-사이클 변경
의뢰인 요청서 등록 -> 해당 지역에 속한 지도사에게 푸시알림(or SMS)
-> 요청서 확인 후 수락 푸시알림(or SMS)+연락처 -> 장례 준비 진행(도우미 지원(2차 배포시 추가))
-> 장례 진행 -> 종료 -> 의뢰인 최종 확인(결제(2차 배포시 추가)) -> 평점/리뷰 작성
-요청서 등록시 해당 지역 지도사에게 알림(연락처 전달?)[]
-요청서 확인 후 수락 푸시알림(or SMS)+지도사프로필[]
-....
NaN
//전역 NaN 속성은 Not-A-Number(숫자가 아님)를 나타냄
//isNaN() 함수로 NaN 여부를 확인
# 주요소스코드
<Client/Join.vue>
<template>
<ion-page>
<ion-custom-header>회원 - 가입</ion-custom-header>
<ion-content :fullscreen="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">회원 - 가입</ion-title>
</ion-toolbar>
</ion-header>
<ion-custom-body class="justify-center">
<div class="logo-box text-center">
<span>
<span class="text-3xl">
<font-awesome-icon icon="lemon" />
</span>
<span class="font-bold text-3xl">
DESIGN LEMON
</span>
</span>
</div>
<form @submit.prevent="checkAndJoin">
<div>
<ion-item>
<ion-label position="stacked">프로필 이미지</ion-label>
<ion-input v-model="joinFormState.profileImg" type="file"></ion-input>
</ion-item>
</div>
<div>
<ion-item>
<ion-label position="floating">아이디</ion-label>
<ion-input v-model="joinFormState.loginId" type="text" minlength="5" maxlength="12" placeholder="아이디를 입력해주세요."></ion-input>
</ion-item>
</div>
<div>
<ion-item>
<ion-label position="floating">비밀번호</ion-label>
<ion-input v-model="joinFormState.loginPw" minlength="8" type="password" placeholder="비밀번호를 입력해주세요."></ion-input>
</ion-item>
</div>
<div>
<ion-item>
<ion-label position="floating">비밀번호 확인</ion-label>
<ion-input v-model="joinFormState.loginPwConfirm" minlength="8" type="password" placeholder="비밀번호 확인을 해주세요."></ion-input>
</ion-item>
</div>
<div>
<ion-item>
<ion-label position="floating">이름</ion-label>
<ion-input v-model="joinFormState.name" minlength="2" placeholder="이름을 입력해주세요."></ion-input>
</ion-item>
</div>
<div>
<ion-item>
<ion-label position="floating">연락처</ion-label>
<ion-input v-model="joinFormState.cellphoneNo" type="tel" maxlength="11" placeholder="연락처를 입력해주세요."></ion-input>
</ion-item>
</div>
<div>
<ion-item>
<ion-label position="floating">이메일</ion-label>
<ion-input v-model="joinFormState.email" type="email" placeholder="이메일을 입력해주세요."></ion-input>
</ion-item>
</div>
<div>
<ion-item>
<ion-label position="floating">지역</ion-label>
<ion-input v-model="joinFormState.region" placeholder="시/도 주소를 입력해주세요."></ion-input>
</ion-item>
</div>
<div class="py-2 px-4">
<ion-button type="submit" expand="block">가입</ion-button>
</div>
<div class="px-4">
<ion-button color="secondary" type="reset" expand="block">초기화</ion-button>
</div>
</form>
</ion-custom-body>
</ion-content>
</ion-page>
</template>
<style>
</style>
<script lang="ts">
import { IonCustomBody, IonCustomHeader } from '@/components/';
import {
IonPage,
IonHeader,
IonToolbar,
IonTitle,
IonContent,
IonLabel,
IonInput,
IonItem,
IonButton,
} from '@ionic/vue';
import { useGlobalState } from '@/stores'
import { useMainService } from '@/services';
import { useRouter } from 'vue-router';
import * as util from '@/utils';
import { reactive } from 'vue';
const useJoinFormState = () => {
return reactive({
profileImg: [] as File[],
loginId: '',
loginPw: '',
loginPwConfirm: '',
name: '',
cellphoneNo: '',
email: '',
region: '',
})
}
export default {
name: 'Join',
components: {
IonHeader,
IonToolbar,
IonTitle,
IonLabel,
IonInput,
IonItem,
IonButton,
IonContent,
IonPage,
IonCustomBody,
IonCustomHeader
},
setup() {
const globalState = useGlobalState();
const joinFormState = useJoinFormState();
const router = useRouter();
const mainService = useMainService();
// function confirmAlert(){
// const msg = '해당 내용으로 가입하시겠습니까?'
// util.showAlertConfirm(msg)
// }
function checkAndJoin() {
// 아이디 체크
const loginId = joinFormState.loginId.trim();
if ( joinFormState.loginId.trim().length == 0 ) {
alert('아이디를 입력해주세요.');
return;
}
// 비번 체크
const loginPw = joinFormState.loginPw.trim();
if ( loginPw.length == 0 ) {
alert('비밀번호를 입력해주세요.');
return;
}
// 비번확인 체크
const loginPwConfirm = joinFormState.loginPwConfirm.trim();
if ( loginPw != loginPwConfirm ) {
alert('비밀번호가 일치하지 않습니다.');
return;
}
// 이름 체크
const name = joinFormState.name.trim();
if ( name.length == 0 ) {
alert('이름을 입력해주세요.');
return;
}
// 전화번호 체크
const cellphoneNo = joinFormState.cellphoneNo.trim();
if ( cellphoneNo.length == 0 ) {
alert('연락처를 입력해주세요.');
return;
}
// 이메일 체크
const email = joinFormState.email.trim();
if ( email.length == 0 ) {
alert('이메일을 입력해주세요.');
return;
}
// 시/도 주소 체크
const region = joinFormState.region.trim();
if ( region.length == 0 ) {
alert('지역(시/도)을 입력해주세요.');
return;
}
async function startFileUpload(onSuccess: Function){
// ! => 반전
// a = undefinded(or null) / !a = true / !!a = flase란 의미
// ? => 만약 profileImgElRef.value?까지가 null이면 여기까지만 실행하겠다라는 의미
// 즉, !!!profileImgElRef.value?.files의 의미는 해당 파일이 없는지 물어보는 것
// 없으면 true
if(joinFormState.profileImg == null){
onSuccess(""); //파일이 없으면 다음 과정 생략하고 onSuccess() 즉시 실행
alert("파일 업로드 안됨")
return;
}
const axRes = await mainService.common_genFile_doUpload(joinFormState.profileImg[0])
if ( axRes.data.fail ) {
util.showAlert(axRes.data.msg);
return;
}
else{
onSuccess(axRes.data.body.genFileIdsStr);
}
}
async function join(loginId: string, loginPw: string, name: string, cellphoneNo: string, email: string, region: string, genFileIdsStr: string) {
const axRes = await mainService.client_doJoin(loginId, loginPw, name, cellphoneNo, email, region, genFileIdsStr);
util.showAlert(axRes.data.msg);
if ( axRes.data.fail ) {
return;
}
router.replace('/client/login?loginId=' + loginId)
}
const startJoin = (genFileIdsStr: string) =>{
join(loginId, loginPw, name, cellphoneNo, email, region, genFileIdsStr);
}
const msg = '해당 내용으로 가입하시겠습니까?'
util.showAlertConfirm(msg).then(confirm => {
if (confirm == false) {
return
} else{
startFileUpload(startJoin);
}
})
}
return {
globalState,
//confirmAlert,
joinFormState,
checkAndJoin,
}
}
}
</script>
<Order/List.vue>
<template>
<ion-custom-header>의뢰 - 리스트</ion-custom-header>
<ion-content >
<ion-list class="mb-12">
<ion-item>
<ion-select v-model="searchState.selectStepLevel">
<ion-select-option value="0">진행단계 전체</ion-select-option>
<ion-select-option value="1">요청서 검토중</ion-select-option>
<ion-select-option value="2">장례준비중</ion-select-option>
<ion-select-option value="3">장례진행중</ion-select-option>
<ion-select-option value="4">장례종료(확인대기중)</ion-select-option>
<ion-select-option value="5">장례종료(최종종료)</ion-select-option>
</ion-select>
</ion-item>
<ion-item>
<ion-select v-model="searchState.searchKeywordType">
<ion-select-option value="deceasedName">고인명</ion-select-option>
<ion-select-option value="bereavedName">상주명</ion-select-option>
<ion-select-option value="extra__clientName">의뢰인명</ion-select-option>
<ion-select-option value="body">내용</ion-select-option>
<ion-select-option value="funeralHome">장례식장</ion-select-option>
</ion-select>
</ion-item>
<ion-item>
<ion-searchbar show-cancel-button="focus" animated inputmode="search" enterkeyhint="enter" placeholder="검색어를 입력해주세요." :value="searchState.searchKeyword" @keyup.enter="onInput($event)"></ion-searchbar>
</ion-item>
<ion-list-header>MyOrderList</ion-list-header>
<template v-bind:key="order.id" v-for="order in returnFilteredOrders">
<ion-item>
<!--진행단계-->
<div v-if="order.stepLevel==1" class="btn-success">
진행단계: {{returnToString(order.stepLevel)}}
</div>
<div v-if="order.stepLevel==2" class="btn-secondary">
진행단계: {{returnToString(order.stepLevel)}}
</div>
<div v-if="order.stepLevel==3" class="btn-warning">
진행단계: {{returnToString(order.stepLevel)}}
</div>
<div v-if="order.stepLevel==4" class="btn-primary">
진행단계: {{returnToString(order.stepLevel)}}
</div>
<div v-if="order.stepLevel==5" class="btn-primary">
진행단계: {{returnToString(order.stepLevel)}}
</div>
<ion-label>
<ion-grid>
<ion-row>
<ion-col size="12" class="bg-gray-300 border rounded-md">
고인명: {{order.deceasedName}}
</ion-col>
</ion-row>
<ion-row>
<ion-col size="4">
상주명: {{order.bereavedName}}
</ion-col>
<ion-col size="10">
의뢰인: {{order.extra__clientName}}
</ion-col>
<ion-col size="10">
담당지도사: {{order.extra__expertName}}
</ion-col>
</ion-row>
<ion-row>
<ion-col size="10">
장례식장: {{order.funeralHome}}
</ion-col>
<ion-col size="10">
인원: {{order.head}} 명
</ion-col>
<ion-col size="10">
종교: {{order.religion}}
</ion-col>
<ion-col size="10">
장례시작일: {{order.startDate}}
</ion-col>
<ion-col size="10">
장례종료일: {{order.endDate}}
</ion-col>
</ion-row>
</ion-grid>
</ion-label>
<div class="flex-col">
<ion-item-divider class="mt-2">
<ion-button color="" slot="end" :href="'/order/detail?id=' + order.id">
상세보기
</ion-button>
</ion-item-divider>
<ion-item-divider class="mt-2">
<ion-button v-if="globalState.loginedClient.id == order.clientId" color="success" slot="end" :href="'/review/add?relTypeCode=expert&relId=' + order.expertId">
후기/평점 작성
</ion-button>
</ion-item-divider>
<ion-item-divider class="mt-2" v-if="globalState.loginedClient.id !== order.clientId">
<ion-button v-if="order.stepLevel==1" color="success" slot="end" @click="changeStepLevel(order.id, order.stepLevel)">
의뢰수락(장례준비)
</ion-button>
<ion-button v-if="order.stepLevel==2" color="success" slot="end" @click="changeStepLevel(order.id, order.stepLevel)">
장례진행
</ion-button>
<ion-button v-if="order.stepLevel==3" color="success" slot="end" @click="changeStepLevel(order.id, order.stepLevel)">
장례종료(확인요청)
</ion-button>
</ion-item-divider>
<ion-item-divider class="mt-2" v-if="globalState.loginedClient.id == order.clientId">
<ion-button v-if="order.stepLevel==2" color="success" slot="end">
장례준비중
</ion-button>
<ion-button v-if="order.stepLevel==3" color="success" slot="end">
장례진행중
</ion-button>
<ion-button v-if="order.stepLevel==4" color="success" slot="end" @click="changeStepLevel(order.id, order.stepLevel)">
장례종료(확인)
</ion-button>
</ion-item-divider>
</div>
</ion-item>
</template>
</ion-list>
</ion-content>
</template>
<style>
</style>
<script lang="ts">
import { IonCustomHeader } from '@/components/';
import {
IonSelect,
IonSelectOption,
IonSearchbar,
IonLabel,
IonListHeader,
IonList,
IonItem,
IonContent,
IonItemDivider,
IonCol,
IonRow,
IonGrid,
IonButton,
} from '@ionic/vue';
import { useGlobalState } from '@/stores'
import { useMainService } from '@/services';
import { reactive, computed, onMounted, watch } from 'vue';
import * as util from '@/utils';
import { useRoute } from 'vue-router';
import { Order } from '@/types';
const useSearchState = () => {
return reactive({
searchKeyword: '',
searchKeywordType: 'deceasedName',
selectStepLevel: '0',
})
}
export default {
name: 'OrderList',
components: {
IonSelect,
IonSelectOption,
IonSearchbar,
IonCustomHeader,
IonLabel,
IonListHeader,
IonList,
IonItem,
IonContent,
IonItemDivider,
IonCol,
IonRow,
IonGrid,
IonButton,
},
setup() {
const globalState = useGlobalState();
const mainService = useMainService();
const searchState = useSearchState();
const route = useRoute();
const state = reactive({
orders: [] as Order[],
});
function returnToString(stepLevel: any) {
let stepLevelToStr = '';
if(stepLevel == 1){
stepLevelToStr = '의뢰요청(의뢰검토중)';
}
if(stepLevel == 2){
stepLevelToStr = '의뢰승인(장례준비중)';
}
if(stepLevel == 3){
stepLevelToStr = '장례진행중';
}
if(stepLevel == 4){
stepLevelToStr = '장례종료(종료확인요청)';
}
if(stepLevel == 5){
stepLevelToStr = '종료확인(최종종료)';
}
return stepLevelToStr;
}
function onInput(event: any){
searchState.searchKeyword = event.target.value;
return searchState.searchKeyword;
}
const returnFilteredOrders = computed(() => {
let filteredOrders = state.orders;
if(searchState.selectStepLevel == '0'){
if(searchState.searchKeywordType == "deceasedName"){
filteredOrders = state.orders.filter((order: Order) => order.deceasedName.includes(searchState.searchKeyword))
}
if(searchState.searchKeywordType == "bereavedName"){
filteredOrders = state.orders.filter((order: Order) => order.bereavedName.includes(searchState.searchKeyword))
}
if(searchState.searchKeywordType == "extra__clientName"){
filteredOrders = state.orders.filter((order: Order) => order.extra__clientName.includes(searchState.searchKeyword))
}
if(searchState.searchKeywordType == "body"){
filteredOrders = state.orders.filter((order: Order) => order.body.includes(searchState.searchKeyword))
}
if(searchState.searchKeywordType == "funeralHome"){
filteredOrders = state.orders.filter((order: Order) => order.funeralHome.includes(searchState.searchKeyword))
}
}
else{
if(searchState.searchKeywordType == "deceasedName"){
filteredOrders = state.orders.filter((order: Order) => order.deceasedName.includes(searchState.searchKeyword) && order.stepLevel === parseInt(searchState.selectStepLevel))
}
if(searchState.searchKeywordType == "bereavedName"){
filteredOrders = state.orders.filter((order: Order) => order.bereavedName.includes(searchState.searchKeyword) && order.stepLevel === parseInt(searchState.selectStepLevel))
}
if(searchState.searchKeywordType == "extra__clientName"){
filteredOrders = state.orders.filter((order: Order) => order.extra__clientName.includes(searchState.searchKeyword) && order.stepLevel === parseInt(searchState.selectStepLevel))
}
if(searchState.searchKeywordType == "body"){
filteredOrders = state.orders.filter((order: Order) => order.body.includes(searchState.searchKeyword) && order.stepLevel === parseInt(searchState.selectStepLevel))
}
if(searchState.searchKeywordType == "funeralHome"){
filteredOrders = state.orders.filter((order: Order) => order.funeralHome.includes(searchState.searchKeyword) && order.stepLevel === parseInt(searchState.selectStepLevel))
}
}
return filteredOrders
})
async function loadOrders(memberId: number, memberType: string){
const axRes = await mainService.order_list(memberId, memberType)
state.orders = axRes.data.body.orders;
}
// function doDeleteReview(id: number) {
// if(confirm('정말 삭제하시겠습니까?') == false){
// return;
// }
// mainService.review_doDelete(id)
// .then(axiosResponse => {
// alert(axiosResponse.data.msg);
// if ( axiosResponse.data.fail ) {
// return;
// }
// window.location.reload();
// });
// }
async function doChangeStepLevel(id: number, stepLevel: number){
const axRes = await mainService.order_changeStepLevel(id, stepLevel)
util.showAlert(axRes.data.msg)
if ( axRes.data.fail ) {
return;
}
window.location.reload();
}
function changeStepLevel(id: number, stepLevel: number){
const msg = '해당 의뢰를 수락하시겠습니까?'
util.showAlertConfirm(msg).then(confirm => {
if (confirm == false) {
return
} else{
doChangeStepLevel(id, stepLevel)
}
})
}
let loginedMemberId = 0;
let loginedMemberType = '';
if(globalState.loginedClient.id != null){
loginedMemberId = globalState.loginedClient.id
loginedMemberType = 'client'
}
// onMounted 바로 실행하는 것이 아닌 모든 것이 준비되었을때 실행됨
onMounted(() => {
//alert("3");
loadOrders(loginedMemberId, loginedMemberType);
// loadReviews(relTypeCode);
});
return {
globalState,
mainService,
state,
searchState,
returnFilteredOrders,
//doDeleteReview,
onInput,
returnToString,
changeStepLevel,
//onClickInput,
}
}
}
</script>