Compare commits

...

2 Commits

4 changed files with 332 additions and 135 deletions

11
package-lock.json generated
View File

@@ -9,6 +9,7 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"core-js": "^3.6.5", "core-js": "^3.6.5",
"cropperjs": "^1.5.13",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"vue": "^2.6.11", "vue": "^2.6.11",
@@ -4907,6 +4908,11 @@
"sha.js": "^2.4.8" "sha.js": "^2.4.8"
} }
}, },
"node_modules/cropperjs": {
"version": "1.5.13",
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.13.tgz",
"integrity": "sha512-by7jKAo73y5/Do0K6sxdTKHgndY0NMjG2bEdgeJxycbcmHuCiMXqw8sxy5C5Y5WTOTcDGmbT7Sr5CgKOXR06OA=="
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "6.0.5", "version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -19708,6 +19714,11 @@
"sha.js": "^2.4.8" "sha.js": "^2.4.8"
} }
}, },
"cropperjs": {
"version": "1.5.13",
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.13.tgz",
"integrity": "sha512-by7jKAo73y5/Do0K6sxdTKHgndY0NMjG2bEdgeJxycbcmHuCiMXqw8sxy5C5Y5WTOTcDGmbT7Sr5CgKOXR06OA=="
},
"cross-spawn": { "cross-spawn": {
"version": "6.0.5", "version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",

View File

@@ -11,6 +11,7 @@
}, },
"dependencies": { "dependencies": {
"core-js": "^3.6.5", "core-js": "^3.6.5",
"cropperjs": "^1.5.13",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"vue": "^2.6.11", "vue": "^2.6.11",

View File

@@ -15,8 +15,12 @@ async function searchAudioContent(searchWord, page) {
) )
} }
async function modifyAudioContent(request) { async function modifyAudioContent(formData) {
return Vue.axios.put("/admin/audio-content", request) return Vue.axios.put("/admin/audio-content", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
})
} }
async function getBannerList(tabId) { async function getBannerList(tabId) {

View File

@@ -125,7 +125,13 @@
:lines="3" :lines="3"
/> />
</td> </td>
<td style="max-width: 200px !important; word-break:break-all; height: auto;"> <td
style="
max-width: 200px !important;
word-break: break-all;
height: auto;
"
>
<vue-show-more-text <vue-show-more-text
:text="item.detail" :text="item.detail"
:lines="3" :lines="3"
@@ -133,7 +139,13 @@
</td> </td>
<td>{{ item.creatorNickname }}</td> <td>{{ item.creatorNickname }}</td>
<td>{{ item.theme }}</td> <td>{{ item.theme }}</td>
<td style="max-width: 100px !important; word-break:break-all; height: auto;"> <td
style="
max-width: 100px !important;
word-break: break-all;
height: auto;
"
>
<vue-show-more-text <vue-show-more-text
:text="item.tags" :text="item.tags"
:lines="3" :lines="3"
@@ -146,14 +158,29 @@
무료 무료
</td> </td>
<td <td
v-if="item.totalContentCount > 0 && item.remainingContentCount > 0" v-if="
style="min-width: 100px !important; word-break:break-all; height: auto;" item.totalContentCount > 0 &&
item.remainingContentCount > 0
"
style="
min-width: 100px !important;
word-break: break-all;
height: auto;
"
> >
{{ item.totalContentCount - item.remainingContentCount }} / {{ item.totalContentCount }} {{ item.totalContentCount - item.remainingContentCount }} /
{{ item.totalContentCount }}
</td> </td>
<td <td
v-else-if="item.totalContentCount > 0 && item.remainingContentCount <= 0" v-else-if="
style="min-width: 100px !important; word-break:break-all; height: auto;" item.totalContentCount > 0 &&
item.remainingContentCount <= 0
"
style="
min-width: 100px !important;
word-break: break-all;
height: auto;
"
> >
Sold Out Sold Out
</td> </td>
@@ -233,9 +260,60 @@
persistent persistent
> >
<v-card> <v-card>
<v-card-title> <v-card-title> 콘텐츠 수정 </v-card-title>
콘텐츠 수정 <v-card-text>
</v-card-title> <v-row align="center">
<v-col cols="4">
커버 이미지
</v-col>
<v-col cols="8">
<v-img
v-if="image_preview"
:src="image_preview"
max-width="200"
aspect-ratio="1"
contain
class="mb-2"
/>
<v-file-input
v-model="cover_image_file"
label="커버 이미지 선택"
accept="image/*"
prepend-icon="mdi-camera"
outlined
dense
@change="onFileChange"
/>
</v-col>
</v-row>
</v-card-text>
<v-card-text v-if="show_cropper">
<v-row>
<v-col cols="12">
<div style="max-height: 400px">
<img
ref="cropper_image"
:src="cropper_src"
style="max-width: 100%"
>
</div>
<v-btn
color="primary"
class="mt-2"
@click="cropImage"
>
크롭 적용
</v-btn>
<v-btn
color="grey"
class="mt-2 ml-2"
@click="cancelCrop"
>
취소
</v-btn>
</v-col>
</v-row>
</v-card-text>
<v-card-text> <v-card-text>
<v-row align="center"> <v-row align="center">
<v-col cols="4"> <v-col cols="4">
@@ -373,16 +451,18 @@
</template> </template>
<script> <script>
import * as api from '@/api/audio_content' import * as api from "@/api/audio_content";
import * as dynamicLink from "@/api/firebase_dynamic_link"; import * as dynamicLink from "@/api/firebase_dynamic_link";
import VuetifyAudio from 'vuetify-audio' import Cropper from "cropperjs";
import VueShowMoreText from 'vue-show-more-text' import "cropperjs/dist/cropper.css";
import VuetifyAudio from "vuetify-audio";
import VueShowMoreText from "vue-show-more-text";
export default { export default {
name: "AudioContentList", name: "AudioContentList",
components: {VuetifyAudio, VueShowMoreText}, components: { VuetifyAudio, VueShowMoreText },
data() { data() {
return { return {
@@ -391,60 +471,135 @@ export default {
show_delete_confirm_dialog: false, show_delete_confirm_dialog: false,
page: 1, page: 1,
total_page: 0, total_page: 0,
status: 'OPEN', status: "OPEN",
search_word: '', search_word: "",
audio_content: {}, audio_content: {},
audio_contents: [], audio_contents: [],
themeList: [], themeList: [],
selected_audio_content: {}, selected_audio_content: {},
utm_source: '', utm_source: "",
utm_medium: '', utm_medium: "",
utm_campaign: '', utm_campaign: "",
} cover_image_file: null,
image_preview: null,
cropper: null,
show_cropper: false,
cropper_src: null,
};
}, },
async created() { async created() {
this.audio_content = {
id: null,
title: "",
detail: "",
theme_id: null,
is_adult: false,
is_comment_available: false,
is_default_cover_image: false,
};
await this.getAudioContentThemeList(); await this.getAudioContentThemeList();
await this.getAudioContent() await this.getAudioContent();
}, },
methods: { methods: {
notifyError(message) { notifyError(message) {
this.$dialog.notify.error(message) this.$dialog.notify.error(message);
}, },
notifySuccess(message) { notifySuccess(message) {
this.$dialog.notify.success(message) this.$dialog.notify.success(message);
}, },
deleteConfirm(item) { deleteConfirm(item) {
this.selected_audio_content = item this.selected_audio_content = item;
this.show_delete_confirm_dialog = true this.show_delete_confirm_dialog = true;
}, },
deleteCancel() { deleteCancel() {
this.selected_audio_content = {} this.selected_audio_content = {};
this.show_delete_confirm_dialog = false this.show_delete_confirm_dialog = false;
}, },
showModifyDialog(item) { showModifyDialog(item) {
this.selected_audio_content = item this.selected_audio_content = item;
this.audio_content.id = item.audioContentId this.audio_content.id = item.audioContentId;
this.audio_content.title = item.title this.audio_content.title = item.title;
this.audio_content.detail = item.detail this.audio_content.detail = item.detail;
this.audio_content.theme_id = item.themeId this.audio_content.theme_id = item.themeId;
this.audio_content.is_adult = item.isAdult this.audio_content.is_adult = item.isAdult;
this.audio_content.is_comment_available = item.isCommentAvailable this.audio_content.is_comment_available = item.isCommentAvailable;
this.audio_content.is_default_cover_image = false this.audio_content.is_default_cover_image = false;
this.show_modify_dialog = true
this.image_preview = item.coverImageUrl;
this.cover_image_file = null;
this.show_modify_dialog = true;
},
onFileChange(file) {
if (!file) {
this.image_preview = this.selected_audio_content.coverImageUrl;
this.show_cropper = false;
return;
}
const reader = new FileReader();
reader.onload = (e) => {
this.cropper_src = e.target.result;
this.show_cropper = true;
this.$nextTick(() => {
if (this.cropper) {
this.cropper.destroy();
}
this.cropper = new Cropper(this.$refs.cropper_image, {
aspectRatio: 1,
viewMode: 1,
});
});
};
reader.readAsDataURL(file);
},
cropImage() {
const canvas = this.cropper.getCroppedCanvas({
width: 500,
height: 500,
});
this.image_preview = canvas.toDataURL();
canvas.toBlob((blob) => {
this.cover_image_file = new File([blob], "cover_image.png", {
type: "image/png",
});
});
this.show_cropper = false;
},
cancelCrop() {
this.show_cropper = false;
this.cover_image_file = null;
this.image_preview = this.selected_audio_content.coverImageUrl;
}, },
cancel() { cancel() {
this.selected_audio_content = {} this.selected_audio_content = {};
this.audio_content = {} this.audio_content = {
this.show_modify_dialog = false id: null,
this.show_delete_confirm_dialog = false title: "",
detail: "",
theme_id: null,
is_adult: false,
is_comment_available: false,
is_default_cover_image: false,
};
this.image_preview = null;
this.cover_image_file = null;
this.show_cropper = false;
if (this.cropper) {
this.cropper.destroy();
this.cropper = null;
}
this.show_modify_dialog = false;
this.show_delete_confirm_dialog = false;
}, },
async modify() { async modify() {
@@ -453,8 +608,8 @@ export default {
this.audio_content.title === undefined || this.audio_content.title === undefined ||
this.audio_content.title.trim().length <= 0 this.audio_content.title.trim().length <= 0
) { ) {
this.notifyError("제목을 입력하세요") this.notifyError("제목을 입력하세요");
return return;
} }
if ( if (
@@ -462,194 +617,220 @@ export default {
this.audio_content.detail === undefined || this.audio_content.detail === undefined ||
this.audio_content.detail.trim().length <= 0 this.audio_content.detail.trim().length <= 0
) { ) {
this.notifyError("내용을 입력하세요") this.notifyError("내용을 입력하세요");
return return;
} }
if (this.is_loading) return; if (this.is_loading) return;
this.isLoading = true this.is_loading = true;
try { try {
const request = { const request = {
id: this.audio_content.id, id: this.audio_content.id,
isDefaultCoverImage: this.audio_content.is_default_cover_image isDefaultCoverImage: this.audio_content.is_default_cover_image,
} };
if ( if (
this.selected_audio_content.title !== this.audio_content.title && this.audio_content.title !== this.selected_audio_content.title &&
this.audio_content.title.trim().length > 0 this.audio_content.title.trim().length > 0
) { ) {
request.title = this.audio_content.title request.title = this.audio_content.title;
}
if (this.audio_content.detail !== this.selected_audio_content.detail) {
request.detail = this.audio_content.detail;
} }
if ( if (
this.selected_audio_content.detail !== this.audio_content.detail && this.audio_content.theme_id !== this.selected_audio_content.themeId
this.audio_content.detail.trim().length > 0
) { ) {
request.detail = this.audio_content.detail request.themeId = this.audio_content.theme_id;
}
if (
this.audio_content.is_adult !== this.selected_audio_content.isAdult
) {
request.isAdult = this.audio_content.is_adult;
}
if (
this.audio_content.is_comment_available !==
this.selected_audio_content.isCommentAvailable
) {
request.isCommentAvailable = this.audio_content.is_comment_available;
} }
if (this.selected_audio_content.themeId !== this.audio_content.theme_id) { const formData = new FormData();
request.themeId = this.audio_content.theme_id formData.append("request", JSON.stringify(request));
if (this.cover_image_file) {
formData.append("coverImage", this.cover_image_file);
} }
if (this.selected_audio_content.isAdult !== this.audio_content.is_adult) { const res = await api.modifyAudioContent(formData);
request.isAdult = this.audio_content.is_adult
}
if (this.selected_audio_content.isCommentAvailable !== this.audio_content.is_comment_available) {
request.isCommentAvailable = this.audio_content.is_comment_available
}
const res = await api.modifyAudioContent(request)
if (res.status === 200 && res.data.success === true) { if (res.status === 200 && res.data.success === true) {
this.cancel() this.cancel();
this.notifySuccess('수정되었습니다.') this.notifySuccess("수정되었습니다.");
this.audio_contents = [] this.audio_contents = [];
await this.getAudioContent() await this.getAudioContent();
} else { } else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') this.notifyError(
res.data.message ||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
} }
} catch (e) { } catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.");
} finally { } finally {
this.is_loading = false this.is_loading = false;
} }
}, },
async deleteAudioContent() { async deleteAudioContent() {
if (this.is_loading) return; if (this.is_loading) return;
this.is_loading = true this.is_loading = true;
try { try {
let request = {id: this.selected_audio_content.audioContentId, isActive: false} let request = {
id: this.selected_audio_content.audioContentId,
isActive: false,
};
const res = await api.modifyAudioContent(request) const res = await api.modifyAudioContent(request);
if (res.status === 200 && res.data.success === true) { if (res.status === 200 && res.data.success === true) {
this.cancel() this.cancel();
this.notifySuccess('삭제되었습니다.') this.notifySuccess("삭제되었습니다.");
this.audio_contents = [] this.audio_contents = [];
await this.getAudioContent() await this.getAudioContent();
} else { } else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') this.notifyError(
res.data.message ||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
} }
} catch (e) { } catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.");
} finally { } finally {
this.is_loading = false this.is_loading = false;
} }
}, },
async next() { async next() {
if (this.search_word.length < 2) { if (this.search_word.length < 2) {
this.search_word = '' this.search_word = "";
await this.getAudioContent() await this.getAudioContent();
} else { } else {
await this.searchAudioContent() await this.searchAudioContent();
} }
}, },
async getAudioContentThemeList() { async getAudioContentThemeList() {
this.is_loading = true this.is_loading = true;
try { try {
const res = await api.getAudioContentThemeList() const res = await api.getAudioContentThemeList();
if (res.status === 200 && res.data.success === true) { if (res.status === 200 && res.data.success === true) {
this.themeList = res.data.data.map((item) => { this.themeList = res.data.data.map((item) => {
return {title: item.theme, value: item.id} return { title: item.theme, value: item.id };
}) });
} else { } else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') this.notifyError(
res.data.message ||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
} }
this.is_loading = false this.is_loading = false;
} catch (e) { } catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.");
this.is_loading = false this.is_loading = false;
} }
}, },
async getAudioContent() { async getAudioContent() {
this.is_loading = true this.is_loading = true;
try { try {
const res = await api.getAudioContentList(this.status, this.page) const res = await api.getAudioContentList(this.status, this.page);
if (res.status === 200 && res.data.success === true) { if (res.status === 200 && res.data.success === true) {
const data = res.data.data const data = res.data.data;
const total_page = Math.ceil(data.totalCount / 10) const total_page = Math.ceil(data.totalCount / 10);
this.audio_contents = data.items this.audio_contents = data.items;
if (total_page <= 0) if (total_page <= 0) this.total_page = 1;
this.total_page = 1 else this.total_page = total_page;
else
this.total_page = total_page
} else { } else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') this.notifyError(
res.data.message ||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
} }
this.is_loading = false this.is_loading = false;
} catch (e) { } catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.");
this.is_loading = false this.is_loading = false;
} }
}, },
async search() { async search() {
this.page = 1 this.page = 1;
await this.searchAudioContent() await this.searchAudioContent();
}, },
async searchAudioContent() { async searchAudioContent() {
if (this.search_word.length === 0) { if (this.search_word.length === 0) {
await this.getAudioContent() await this.getAudioContent();
} else if (this.search_word.length < 2) { } else if (this.search_word.length < 2) {
this.notifyError('검색어를 2글자 이상 입력하세요.') this.notifyError("검색어를 2글자 이상 입력하세요.");
} else { } else {
this.is_loading = true this.is_loading = true;
try { try {
const res = await api.searchAudioContent(this.search_word, this.page) const res = await api.searchAudioContent(this.search_word, this.page);
if (res.status === 200 && res.data.success === true) { if (res.status === 200 && res.data.success === true) {
const data = res.data.data const data = res.data.data;
const total_page = Math.ceil(data.totalCount / 10) const total_page = Math.ceil(data.totalCount / 10);
this.audio_contents = data.items this.audio_contents = data.items;
if (total_page <= 0) if (total_page <= 0) this.total_page = 1;
this.total_page = 1 else this.total_page = total_page;
else
this.total_page = total_page
} else { } else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') this.notifyError(
res.data.message ||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
} }
this.is_loading = false this.is_loading = false;
} catch (e) { } catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') this.notifyError(
this.is_loading = false "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
this.is_loading = false;
} }
} }
}, },
async shareAudioContent(item) { async shareAudioContent(item) {
this.is_loading = true this.is_loading = true;
try { try {
const linkData = await dynamicLink.shareAudioContent(item, this.utm_source, this.utm_medium, this.utm_campaign); const linkData = await dynamicLink.shareAudioContent(
item,
this.utm_source,
this.utm_medium,
this.utm_campaign
);
if (linkData.status === 200) { if (linkData.status === 200) {
await navigator.clipboard.writeText(linkData.data.shortLink) await navigator.clipboard.writeText(linkData.data.shortLink);
this.notifySuccess("링크가 복사되었습니다.") this.notifySuccess("링크가 복사되었습니다.");
} else { } else {
this.notifyError("링크를 생성하지 못했습니다.") this.notifyError("링크를 생성하지 못했습니다.");
} }
} finally { } finally {
this.is_loading = false this.is_loading = false;
} }
}, },
} },
} };
</script> </script>
<style scoped> <style scoped></style>
</style>