<div class="list">
<h3 class="mb-16">도슨트</h3>
<div class="list-top mb-16">
<SButton>삭제</SButton>
<SButton button-type="primary" @click="onRegisterDocentModal(true)">등록</SButton>
</div>
<table class="admin-table">
<thead>
<tr>
<th><SCheckbox /></th>
<th>썸네일</th>
<th>작품명</th>
<th>소개글</th>
<th>지도</th>
<th>오디오</th>
<th>수정</th>
</tr
</thead>
<tbody v-if="!detailData.infos || !detailData.infos[0]">
<tr>
<td colspan="11"><div>리스트가 없습니다.</div></td>
</tr>
</tbody>
<Draggable v-bind="dragOptions" v-model="detailData.infos" tag="tbody">
<tr v-for="(item, index) in detailData.infos" :key="item.id">
<td>
<div>
<SCheckbox />
</div>
</td>
<td>
<div class="img">
<img :src="`${$store.state.BASE_URL}${item.thumbnail?.savedFileName}`" alt="테스트" />
</div>
</td>
<td>
<div class="text-left">
{{ item.title }}
</div>
</td>
<td>
<div class="text-left">
{{ item.intro }}
</div>
</td>
<td>
<div>
{{ item.map ? 'Y' : 'N' }}
</div>
</td>
<td>
<div>
{{ item.audio ? 'Y' : 'N' }}
</div>
</td>
<td>
<div>
<SButton w-size="small" @click="onRegisterDocentModal(false, index)">수정</SButton>
</div>
</td>
</tr>
</Draggable>
</table>
</div>
<DocentRegistModal
:is-show="modal.isRegistDocent"
:docent-data="selectedDocent"
@close="modal.isRegistDocent = false"
@save="ModifiedDocentModal"
/>
<template>
<transition name="fade">
<div v-if="isShow" class="modal-wrap">
<div class="modal-inner">
<div class="modal">
<div class="head">
<button type="button" class="close-btn" @click="cancel">
<i class="ic-close" />
</button>
</div>
<div class="body">
<div class="contents-info mb-24">
<div class="representative-img mr-16">
<label class="upload-img" @click.stop>
<img
v-if="thumbnail?.savedFileName"
:src="`${$store.state.BASE_URL}${thumbnail?.savedFileName}`"
:alt="`${thumbnail?.originalFileName}`"
/>
<span v-if="!thumbnail?.savedFileName" class="file-info">
<i class="ic-plus mb-16"></i>
<span class="bt-2r">이미지를 등록해주세요.</span>
<span class="nt-5">5MB 이하의 JPG, GIF, PNG파일을 선택해주세요.</span>
<input
type="file"
accept=".jpg, .png, .gif"
class="is-blind"
@change="fileUpload($event, 'thumbnail')"
/>
</span>
</label>
<SButton
v-if="thumbnail?.savedFileName"
button-type="primary"
w-size="small"
h-size="small"
@click.self="onDeleteFile('thumbnail')"
>
삭제
</SButton>
</div>
<div class="works-info">
<SInput v-model="title" w-size="full" class="mb-14" placeholder="제목" />
<SInput v-model="writer" w-size="full" class="mb-14" placeholder="작가명" />
<SInput v-model="position" w-size="full" placeholder="작품위치" />
</div>
</div>
<div class="mb-24">
<STextarea v-model="intro" placeholder="작품 소개" />
</div>
<div class="mb-12">
<div class="file-list">
<label class="mt-2m">음성파일</label>
<SInput :value="getFileName('audio')" class="file-input mr-8" readonly />
<SButton v-if="!audio" @click="onFileClick('audioUpload')">파일 첨부</SButton>
<template v-else>
<a
:href="`/api/file/download?path=${audio.savedFileName}&fileName=${audio.originalFileName}`"
download
class="download primary mr-8"
>다운로드</a
>
<SButton w-size="small" @click="onDeleteFile('audio')">삭제</SButton>
</template>
<input
ref="audioUpload"
type="file"
accept=".mp3, .wave"
class="is-blind"
@change="fileUpload($event, 'audio')"
/>
</div>
<p class="bt-2r">15MB 이하의 MP3, WAVE 파일</p>
</div>
<div>
<div class="file-list">
<label class="mt-2m">지도 이미지</label>
<SInput :value="getFileName('map')" class="file-input mr-8" readonly />
<SButton v-if="!map" @click="onFileClick('mapUpload')">파일 첨부</SButton>
<template v-else>
<a
:href="`/api/file/download?path=${map.savedFileName}&fileName=${map.originalFileName}`"
download
class="download primary mr-8"
>다운로드</a
>
<SButton w-size="small" @click="onDeleteFile('map')">삭제</SButton>
</template>
<input
ref="mapUpload"
type="file"
accept=".png, .jpg, .jpeg, .gif"
class="is-blind"
@change="fileUpload($event, 'map')"
/>
</div>
<p class="bt-2r">{가로}px {세로}px 5MB 이하의 PNG, JPG, JPEG, GIF 파일</p>
</div>
</div>
<div class="foot-wrap">
<div class="foot">
<SButton class="mr-8" @click="cancel">취소</SButton>
<SButton button-type="primary" @click="saveDocent">완료</SButton>
</div>
</div>
</div>
</div>
<SDialogModal no-scroll-lock :is-show="isShowErrorModal" @close="isShowErrorModal = false">
<template #content>{{ errorModalMsg }}</template>
<template #modal-btn2>
<SButton button-type="primary" @click="isShowErrorModal = false">확인</SButton>
</template>
</SDialogModal>
</div>
</transition>
</template>
default로 넣어줄 data 를 만들어준다.
isNew의 boolean 값에 따라 기존 data를 가져오거나 defaultData를 넣어줘 등록과 수정을 구분하고 targetDocent
를 selectedDocent
에 담아 props로 전달
<script>
export default {
async asyncData({ params, $axios, redirect, $dayjs }) {
const { id } = params;
const isNew = id == null;
const detailData = id
? await $axios
.$get(`/admin/docents/${id}`)
.then((detailData) => ({
...detailData,
startDate: $dayjs(detailData.startDate).format('YYYY-MM-DD'),
endDate: $dayjs(detailData.endDate).format('YYYY-MM-DD')
}))
.catch(() => {
redirect('/admin/common/docent');
})
: cloneDeep(DOCENTS_DETAIL);
return { id, isNew, detailData };
},
data() {
return {
isNew: false,
detailData: null,
selectedDocent: null,
}
},
methods: {
onRegisterDocentModal(isNew, index) {
const defaultDocent = {
thumbnail: null,
title: '',
writer: '',
position: '',
intro: '',
audio: null,
map: null
};
const targetDocent = !isNew ? this.detailData.infos[index] : defaultDocent;
this.selectedDocent = {
...targetDocent,
isNew,
index
};
this.modal.isRegistDocent = true;
},
}
}
</script>
props로 받은 default 값 정의 후 watch로 isShow를 감지하여 data 넣어줌,
resetData() 함수로 취소 시에 모달에 작성해놓은 data들을 다시 원 상태로 되돌려준다.
<script>
export default {
props: {
docentData: {
type: Object,
required: false,
default: () => ({
isNew: true,
index: null,
thumbnail: null,
title: '',
writer: '',
position: '',
intro: '',
audio: null,
map: null
})
},
data() {
return {
isNew: true,
thumbnail: null,
title: '',
writer: '',
position: '',
intro: '',
audio: null,
map: null,
isShowErrorModal: false,
errorModalMsg: ''
};
},
watch: {
isShow(newValue) {
console.log(newValue);
if (newValue) {
this.isNew = this.docentData.isNew;
this.thumbnail = this.docentData.thumbnail;
this.title = this.docentData.title;
this.writer = this.docentData.writer;
this.position = this.docentData.position;
this.intro = this.docentData.intro;
this.audio = this.docentData.audio;
this.map = this.docentData.map;
}
}
},
methods: {
resetData() {
this.thumbnail = null;
this.title = '';
this.writer = '';
this.position = '';
this.intro = '';
this.audio = null;
this.map = null;
},
cancel() {
this.resetData();
this.$emit('close');
},
}
}
}
</script>
벨리데이션 체크 후 통과 시 data 를 담아서 $emit으로 전달
saveDocent() {
if (this.isValidate()) {
const newDocent = {
thumbnail: this.thumbnail,
title: this.title,
writer: this.writer,
position: this.position,
intro: this.intro,
audio: this.audio,
map: this.map
};
this.$emit('save', { newDocent, isNew: this.isNew, index: this.docentData.index });
this.resetData();
}
},
newDocent를 받아 등록일 시 infos에 push 시켜주고 수정일땐 덮어씌워줌
modifiedDocentModal({ newDocent, isNew, index }) {
if (isNew) {
this.detailData.infos.push(newDocent);
} else {
this.detailData.infos[index] = {
...this.detailData.infos[index],
...newDocent
};
}
this.modal.isRegistDocent = false;
},