97 CSS module, scoped
source: categories/study/vue-experiance/vue-experiance_9-97.md
97 CSS module, scoped
<template>
<div
ref="noticePopup"
:class="[$style.map_overlay, isClassName]"
:style="{ position: 'fixed', zIndex: 100, top, left }"
>
<overlay-scroll :style="{ maxHeight: '221px' }">
<li v-for="(item, index) in noticeDetailData.data" :key="index">
<h1 :class="$style.title">
<span :class="$style.txt">알림 {{ index + 1 }}</span>
</h1>
<dl :class="$style.info_list">
<div :class="$style.info_area">
<dt :class="$style.info_title">센서타입</dt>
<dd :class="$style.info_cont">{{ item.sensorTypeName }}</dd>
</div>
<div :class="$style.info_area">
<dt :class="$style.info_title">센서명</dt>
<dd :class="$style.info_cont">{{ item.sensorName }}</dd>
</div>
<div :class="$style.info_area">
<dt :class="$style.info_title">
{{ noticeType === 'sensor' ? '품목구분' : '알림구분' }}
</dt>
<dd :class="$style.info_cont">{{ item.noticeDivisionName }}</dd>
</div>
<div :class="$style.info_area">
<dt :class="$style.info_title">임계치</dt>
<dd :class="$style.info_cont">{{ item.limitValueName }}</dd>
</div>
<div :class="$style.info_area">
<dt :class="$style.info_title">측정값</dt>
<dd :class="$style.info_cont">{{ item.measureValueName }}</dd>
</div>
<div :class="$style.info_area">
<dt :class="$style.info_title">발생일시</dt>
<dd :class="$style.info_cont">{{ item.measureTime }}</dd>
</div>
<div :class="$style.info_area">
<dt :class="$style.info_title">알림내용</dt>
<dd :class="$style.info_cont">{{ item.content }}</dd>
</div>
<div :class="$style.info_area">
<dt :class="$style.info_title">발생위치</dt>
<dd :class="$style.info_cont">{{ item.noticeAddr }}</dd>
</div>
</dl>
</li>
</overlay-scroll>
<div :class="$style.btn_area">
<button
type="button"
:class="$style.btn"
@click="closeTransitNoticePopup"
>
닫기
</button>
<button
type="button"
:class="[$style.btn, $style.black]"
@click="clickDetail"
>
상세보기
</button>
</div>
</div>
</template>
<script>
import OverlayScroll from '@/components/renew/reuse/OverlayScroll';
import { EventBus } from '@/plugins/eventBus';
export default {
name: 'NoticePopup',
components: {
OverlayScroll,
},
props: {
noticeDetailData: {
type: Object,
required: true,
},
noticeType: {
type: String,
},
target: {
type: HTMLButtonElement,
required: true,
},
className: {
type: String,
default: '',
},
},
data() {
return {
start: null,
progress: null,
top: 0,
left: 0,
};
},
computed: {
isClassName() {
console.log(this.className.split(' ').join(' '));
return this.className.split(' ').join(' ');
},
},
mounted() {
requestAnimationFrame(this.positionUpdate);
},
methods: {
closeTransitNoticePopup() {
this.$emit('click:closeNoticePopup');
},
clickDetail() {
EventBus.$emit(
'clickNoticeDetail',
'notification-history',
this.noticeDetailData,
);
this.$emit('click:closeNoticePopup');
},
positionUpdate() {
if (!this.target || !this.$refs.noticePopup) {
return;
}
const targetParentRect =
this.target.parentElement.getBoundingClientRect();
const targetRect = this.target.getBoundingClientRect();
this.top = targetRect.top + targetRect.height + 'px';
this.left =
targetParentRect.left +
targetParentRect.width -
this.$refs.noticePopup.getBoundingClientRect().width -
10 +
'px';
requestAnimationFrame(this.positionUpdate);
},
},
};
</script>
<style module lang="scss">
.map_overlay {
box-sizing: border-box;
width: 280px;
border-radius: 8px;
background-color: #fff;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.2);
text-align: left;
.title {
padding: 16px 16px 0;
font-size: 0;
.txt {
color: #222;
font-weight: normal;
font-size: 14px;
line-height: 21px;
vertical-align: middle;
}
}
.info_list {
margin-top: 10px;
padding: 0 16px;
}
.info_area {
display: flex;
.info_title {
flex: 0 0 88px;
color: #a1a1a1;
font-size: 12px;
line-height: 22px;
}
.info_cont {
flex: 1 1 auto;
margin-left: 8px;
color: #555;
font-size: 12px;
line-height: 22px;
}
}
.btn_area {
box-sizing: border-box;
padding: 16px 0;
font-size: 0;
text-align: center;
.btn {
padding: 3px 10px;
border: 1px solid #e1e1e1;
color: #262626;
font-size: 12px;
line-height: 18px;
&.black {
border-color: #555;
background-color: #555;
color: #fff;
}
&:not(:first-child) {
margin-left: 4px;
}
}
}
}
</style>
<template>
<div v-if="noPlan" class="no_result_area">
<div class="no_result">
<div>
<p class="txt">운행중인 운송정보가 없습니다.</p>
<btn-anchor
:type="'button'"
:class-name="'bg_yellow'"
@btnAnchorClick="openCreateTransitPlanPopup"
>
운송계획 등록하기
</btn-anchor>
</div>
</div>
</div>
<div v-else>
<div class="header">
<span class="title">운송정보</span>
<div v-if="btnVisible" class="btn_area">
<btn-anchor
v-if="boardPlanTestMode"
:class-name="'bg_white'"
:type="'button'"
:is-disabled="notAvailableModifyStatus"
@btnAnchorClick="boardPlanTestModeNextStep"
>
운송전/운송출발
</btn-anchor>
<btn-anchor
v-if="!notAvailableCancel"
:class-name="'bg_white'"
:type="'button'"
:is-disabled="notAvailableCancel"
@btnAnchorClick="openCancelTransitPlanPopup"
>
운송취소
</btn-anchor>
<btn-anchor
v-if="!notAvailableModify"
:class-name="'bg_white'"
:type="'button'"
:is-disabled="notAvailableModify"
@btnAnchorClick="openModifyTransitPlanPopup"
>
수정
</btn-anchor>
</div>
</div>
<dl class="plan_info_list">
<div class="plan_info_item">
<dt class="tit">운송계획 등록자</dt>
<dd class="cont">{{ boardPlanInfo.regMemberName }}</dd>
</div>
<div class="plan_info_item">
<dt class="tit">운송계획 등록일시</dt>
<dd class="cont">{{ boardPlanInfo.regDt }}</dd>
</div>
<div class="plan_info_item">
<dt class="tit">출발 예정 일시</dt>
<dd class="cont">{{ boardPlanInfo.startDt }}</dd>
</div>
<div class="plan_info_item">
<dt class="tit">운송내용</dt>
<dd class="cont">{{ boardPlanInfo.transitContent }}</dd>
</div>
</dl>
<table-section>
<template #header>
<span class="title">운송지</span>
</template>
<template #table>
<overlay-scroll>
<table class="table table_normal table_row_type">
<caption>
<span class="screen_out">운송지 테이블</span>
</caption>
<colgroup>
<col style="width: 60px" />
<col style="width: 224px" />
<col style="width: 64px" />
<col style="width: 112px" />
<col style="width: 64px" />
<col style="width: 96px" />
<col style="width: 68px" />
</colgroup>
<thead>
<tr>
<th scope="col">구분</th>
<th scope="col">주소</th>
<th scope="col">수령인</th>
<th scope="col">수령인 연락처</th>
<th scope="col">상태</th>
<th scope="col">출발/도착시간</th>
<th scope="col">이상알림</th>
</tr>
</thead>
<tbody>
<tr
v-if="
!boardPlanInfo.locationList ||
!boardPlanInfo.locationList.length
"
>
<td colspan="7">운송계획 운송지 정보가 없습니다.</td>
</tr>
<tr
v-for="(item, index) in boardPlanInfo.locationList"
v-else
:key="index"
:class="{ is_error: item.countWarns }"
>
<td>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ getLocationTypeName(index) }}
</div>
</c-tooltip>
</td>
<td>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ getAddr(item) }}
</div>
</c-tooltip>
</td>
<td>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ item.name | checkEmptyData }}
</div>
</c-tooltip>
</td>
<td>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ item.contactNo | convertPhoneNumber | checkEmptyData }}
</div>
</c-tooltip>
</td>
<td v-if="availableLocationStatusChange(item)">
<btn-anchor
v-if="!vehicleInfo.params.isCancelPlan"
:type="'button'"
:class-name="'bg_black small'"
@btnAnchorClick="changeLocationStatus(item)"
>도착</btn-anchor
>
<p v-else>-</p>
</td>
<td v-else>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ getStatusName(item) }}
</div>
</c-tooltip>
</td>
<td>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ item.time | dateTimeFormatDash2 | checkEmptyData }}
</div>
</c-tooltip>
</td>
<td>
<template v-if="item.countWarns">
<btn-anchor
:type="'button'"
:class-name="'link is_error'"
@btnAnchorClick="clickNoticePopup2($event, item)"
>{{ item.countWarns }}</btn-anchor
>
</template>
<template v-else>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ item.countWarns }}
</div>
</c-tooltip>
</template>
</td>
</tr>
</tbody>
</table>
</overlay-scroll>
</template>
</table-section>
<table-section>
<template #header>
<span class="title">품목정보</span>
</template>
<template #table>
<table class="table table_normal table_row_type">
<caption>
<span class="screen_out">품목정보 테이블</span>
</caption>
<colgroup>
<col style="width: 60px" />
<col style="width: 220px" />
<col style="width: 88px" />
<col style="width: 64px" />
<col style="width: 96px" />
<col style="width: 160px" />
</colgroup>
<thead>
<tr>
<th scope="col">번호</th>
<th scope="col">물품명</th>
<th scope="col">수량</th>
<th scope="col">단위</th>
<th scope="col">운송지</th>
<th scope="col">비고</th>
</tr>
</thead>
<tbody>
<tr
v-if="!boardPlanInfo.goodsList || !boardPlanInfo.goodsList.length"
>
<td colspan="6">운송계획 품목 정보가 없습니다.</td>
</tr>
<tr
v-for="(item, index) in boardPlanInfo.goodsList"
v-else
:key="index"
>
<td>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ index + 1 }}
</div>
</c-tooltip>
</td>
<td>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ item.name }}
</div>
</c-tooltip>
</td>
<td>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ item.qty }}
</div>
</c-tooltip>
</td>
<td>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ item.unit }}
</div>
</c-tooltip>
</td>
<td>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ item.locationTypeName }}
</div>
</c-tooltip>
</td>
<td>
<c-tooltip>
<div class="text-overflow-ellipsis">
{{ item.remark | checkEmptyData }}
</div>
</c-tooltip>
</td>
</tr>
</tbody>
</table>
</template>
</table-section>
<!-- [S] 이상알림 팝업 -->
<div>
<notice-popup
v-if="noticeDetailData.activeNoticePopup"
:target="noticePopupTarget"
:notice-detail-data="noticeDetailData"
:class-name="$style.custom"
@click:closeNoticePopup="closeTransitNoticePopup"
/>
</div>
<!-- // [E] 이상알림 팝업 -->
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
import TableSection from '@/components/renew/reuse/TableSection';
import BtnAnchor from '@/components/renew/reuse/BtnAnchor';
import { EventBus } from '@/plugins/eventBus';
import request from '@/api/index';
import OverlayScroll from '@/components/renew/reuse/OverlayScroll';
import CTooltip from '@/components/renew/reuse/CTooltip';
import NoticePopup from '@/components/renew/reuse/NoticePopup';
export default {
name: 'PartStatusTransitPopupDetailVehicleInfo',
components: {
OverlayScroll,
TableSection,
BtnAnchor,
CTooltip,
NoticePopup,
},
props: {
vehicleInfo: {
type: Object,
required: true,
},
},
data() {
return {
boardPlanInfo: {},
noPlan: false,
boardPlanTestMode: false,
isBtnVisible: true,
noticeDetailData: {
partSeq: '',
vehicleSeq: '',
seq: '',
startDt: '',
activeNoticePopup: false,
data: [],
},
noticePopupTarget: null,
};
},
computed: {
...mapGetters('transit', ['boardPlan']),
getLocationTypeName() {
return index => {
const maxLength = this.boardPlanInfo.locationList.length - 1;
switch (index) {
case 0:
return '출발지';
case maxLength:
return '도착지';
default:
return '경유지';
}
};
},
getAddr() {
return item => `(${item.zipcode}) ${item.addr}`;
},
// 현재 계획 수정가능 여부
notAvailableModify() {
return !['WAIT', 'BEFORE'].includes(this.boardPlanInfo.status);
},
// 계획 임의 상태 변경여부
notAvailableModifyStatus() {
return !['WAIT', 'BEFORE'].includes(this.boardPlanInfo.status);
},
// 도착상태 변경 가능여부
availableLocationStatusChange() {
return item => {
// 최초 출발지 경유지로 출발은 '모바일'에서만 수행
if (['BEFORE', 'WAIT'].includes(this.vehicleInfo.status)) {
return false;
}
// 출발지가 아니고 출발 전, 출발 상태일 경우 도착 상태 변경 가능
return (
item.locationType !== 'START' &&
['PLS00', 'PLS01'].includes(item.status)
);
};
},
notAvailableCancel() {
return this.vehicleInfo.status === 'DONE';
},
//팝업 우측 상단 액션 버튼 노출 여부
btnVisible() {
const result = this.vehicleInfo.isBtnVisible === undefined;
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
return (this.isBtnVisible = result);
},
// 상태이름 출력
getStatusName() {
return item => (item.status === 'PLS03' ? '-' : item.statusName);
},
},
watch: {
boardPlan(val) {
this.boardPlanInfo = { ...val };
},
},
created() {
this.noPlan = !this.vehicleInfo.params.planSeq;
if (!this.noPlan) {
// 운송계획 정보 조회
this.getBoardPlan(this.vehicleInfo);
}
},
methods: {
...mapActions('transit', ['getBoardPlan']),
// 운송취소 팝업 열기
openCancelTransitPlanPopup() {
const { vehicleSeq, params } = this.vehicleInfo;
const { partSeq, startDt, planSeq } = params;
EventBus.$emit('open:canceltransitplan', {
before: 'PartStatusTransitPopupDetailVehicleInfo',
origin: this.vehicleInfo,
vehicleSeq,
params: {
partSeq,
startDt,
seq: planSeq,
},
});
},
// 운송계획 수정 열기 (현재 운송계획 수정)
openModifyTransitPlanPopup() {
const params = {
vehicleSeq: this.vehicleInfo.vehicleSeq,
seq: this.vehicleInfo.params.planSeq,
...this.vehicleInfo.params,
};
EventBus.$emit('open:modifytransitplan', params, true);
EventBus.$emit('close:transitpopupdetail');
},
// 운송계획 등록 팝업 열기
openCreateTransitPlanPopup() {
EventBus.$emit('open:createtransitplan', {
vehicleSeq: this.vehicleInfo.vehicleSeq,
});
EventBus.$emit('close:transitpopupdetail');
},
// 운송계획 다음 상태로 변경 (운송전 / 운송중)
async boardPlanTestModeNextStep() {
const vehicleSeq = this.vehicleInfo.vehicleSeq;
const { partSeq, startDt, planSeq } = this.vehicleInfo.params;
const status = this.boardPlanInfo.status;
if (status === 'WAIT') {
const testUrl = `/transit/board/plan/reserve/${vehicleSeq}`;
// 운송전 상태로
await request.put(testUrl, {
partSeq,
startDt,
seq: planSeq,
});
this.$toasted.show('[TEST MODE] 차량이 운송전 상태로 변경되었습니다.');
}
if (status === 'BEFORE') {
const testUrl = `/transit/board/plan/status/${vehicleSeq}`;
// 운송중 상태로
await request.put(testUrl, {
partSeq,
startDt,
planSeq,
seq: 1,
});
this.$toasted.show('[TEST MODE] 차량이 운송중 상태로 변경되었습니다.');
}
EventBus.$emit('refresh:kanbancontents');
EventBus.$emit('close:transitpopupdetail');
},
clickNoticePopup3(e, item) {
EventBus.$emit('click:noticePopup', e, item);
},
// 위치정보 수정 (도착상태 변경)
changeLocationStatus(item) {
EventBus.$emit('open:changelocationstatus', item);
},
//이상알림 레이어팝업
clickNoticePopup2({ target }, item) {
// 차량 이상정보 갱신
this.noticePopupTarget = target;
this.noticeDetailData.activeNoticePopup = false;
this.noticeDetailData.data = item.warnList;
this.noticeDetailData.partSeq = item.partSeq;
this.noticeDetailData.vehicleSeq = item.vehicleSeq;
this.noticeDetailData.startDt = item.startDt;
this.noticeDetailData.planSeq = item.planSeq;
this.noticeDetailData.activeNoticePopup = true;
},
closeTransitNoticePopup() {
this.noticeDetailData.activeNoticePopup = false;
},
},
};
</script>
<style module lang="scss">
//.custom {
// background-color: #000;
//
// [class^='NoticePopup_txt'] {
// color: #fff;
// }
//}
</style>