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",
"dependencies": {
"core-js": "^3.6.5",
"cropperjs": "^1.5.13",
"file-saver": "^2.0.5",
"lodash": "^4.17.21",
"vue": "^2.6.11",
@@ -4907,6 +4908,11 @@
"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": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -19708,6 +19714,11 @@
"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": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",

View File

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

View File

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

View File

@@ -125,7 +125,13 @@
:lines="3"
/>
</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
:text="item.detail"
:lines="3"
@@ -133,7 +139,13 @@
</td>
<td>{{ item.creatorNickname }}</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
:text="item.tags"
:lines="3"
@@ -146,14 +158,29 @@
무료
</td>
<td
v-if="item.totalContentCount > 0 && item.remainingContentCount > 0"
style="min-width: 100px !important; word-break:break-all; height: auto;"
v-if="
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
v-else-if="item.totalContentCount > 0 && item.remainingContentCount <= 0"
style="min-width: 100px !important; word-break:break-all; height: auto;"
v-else-if="
item.totalContentCount > 0 &&
item.remainingContentCount <= 0
"
style="
min-width: 100px !important;
word-break: break-all;
height: auto;
"
>
Sold Out
</td>
@@ -233,9 +260,60 @@
persistent
>
<v-card>
<v-card-title>
콘텐츠 수정
</v-card-title>
<v-card-title> 콘텐츠 수정 </v-card-title>
<v-card-text>
<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-row align="center">
<v-col cols="4">
@@ -373,11 +451,13 @@
</template>
<script>
import * as api from '@/api/audio_content'
import * as api from "@/api/audio_content";
import * as dynamicLink from "@/api/firebase_dynamic_link";
import VuetifyAudio from 'vuetify-audio'
import VueShowMoreText from 'vue-show-more-text'
import Cropper from "cropperjs";
import "cropperjs/dist/cropper.css";
import VuetifyAudio from "vuetify-audio";
import VueShowMoreText from "vue-show-more-text";
export default {
name: "AudioContentList",
@@ -391,60 +471,135 @@ export default {
show_delete_confirm_dialog: false,
page: 1,
total_page: 0,
status: 'OPEN',
search_word: '',
status: "OPEN",
search_word: "",
audio_content: {},
audio_contents: [],
themeList: [],
selected_audio_content: {},
utm_source: '',
utm_medium: '',
utm_campaign: '',
}
utm_source: "",
utm_medium: "",
utm_campaign: "",
cover_image_file: null,
image_preview: null,
cropper: null,
show_cropper: false,
cropper_src: null,
};
},
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.getAudioContent()
await this.getAudioContent();
},
methods: {
notifyError(message) {
this.$dialog.notify.error(message)
this.$dialog.notify.error(message);
},
notifySuccess(message) {
this.$dialog.notify.success(message)
this.$dialog.notify.success(message);
},
deleteConfirm(item) {
this.selected_audio_content = item
this.show_delete_confirm_dialog = true
this.selected_audio_content = item;
this.show_delete_confirm_dialog = true;
},
deleteCancel() {
this.selected_audio_content = {}
this.show_delete_confirm_dialog = false
this.selected_audio_content = {};
this.show_delete_confirm_dialog = false;
},
showModifyDialog(item) {
this.selected_audio_content = item
this.selected_audio_content = item;
this.audio_content.id = item.audioContentId
this.audio_content.title = item.title
this.audio_content.detail = item.detail
this.audio_content.theme_id = item.themeId
this.audio_content.is_adult = item.isAdult
this.audio_content.is_comment_available = item.isCommentAvailable
this.audio_content.is_default_cover_image = false
this.show_modify_dialog = true
this.audio_content.id = item.audioContentId;
this.audio_content.title = item.title;
this.audio_content.detail = item.detail;
this.audio_content.theme_id = item.themeId;
this.audio_content.is_adult = item.isAdult;
this.audio_content.is_comment_available = item.isCommentAvailable;
this.audio_content.is_default_cover_image = false;
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() {
this.selected_audio_content = {}
this.audio_content = {}
this.show_modify_dialog = false
this.show_delete_confirm_dialog = false
this.selected_audio_content = {};
this.audio_content = {
id: null,
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() {
@@ -453,8 +608,8 @@ export default {
this.audio_content.title === undefined ||
this.audio_content.title.trim().length <= 0
) {
this.notifyError("제목을 입력하세요")
return
this.notifyError("제목을 입력하세요");
return;
}
if (
@@ -462,194 +617,220 @@ export default {
this.audio_content.detail === undefined ||
this.audio_content.detail.trim().length <= 0
) {
this.notifyError("내용을 입력하세요")
return
this.notifyError("내용을 입력하세요");
return;
}
if (this.is_loading) return;
this.isLoading = true
this.is_loading = true;
try {
const request = {
id: this.audio_content.id,
isDefaultCoverImage: this.audio_content.is_default_cover_image
}
isDefaultCoverImage: this.audio_content.is_default_cover_image,
};
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
) {
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 (
this.selected_audio_content.detail !== this.audio_content.detail &&
this.audio_content.detail.trim().length > 0
this.audio_content.theme_id !== this.selected_audio_content.themeId
) {
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) {
request.themeId = this.audio_content.theme_id
const formData = new FormData();
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) {
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)
const res = await api.modifyAudioContent(formData);
if (res.status === 200 && res.data.success === true) {
this.cancel()
this.notifySuccess('수정되었습니다.')
this.cancel();
this.notifySuccess("수정되었습니다.");
this.audio_contents = []
await this.getAudioContent()
this.audio_contents = [];
await this.getAudioContent();
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(
res.data.message ||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
}
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.");
} finally {
this.is_loading = false
this.is_loading = false;
}
},
async deleteAudioContent() {
if (this.is_loading) return;
this.is_loading = true
this.is_loading = true;
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) {
this.cancel()
this.notifySuccess('삭제되었습니다.')
this.cancel();
this.notifySuccess("삭제되었습니다.");
this.audio_contents = []
await this.getAudioContent()
this.audio_contents = [];
await this.getAudioContent();
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(
res.data.message ||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
}
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.");
} finally {
this.is_loading = false
this.is_loading = false;
}
},
async next() {
if (this.search_word.length < 2) {
this.search_word = ''
await this.getAudioContent()
this.search_word = "";
await this.getAudioContent();
} else {
await this.searchAudioContent()
await this.searchAudioContent();
}
},
async getAudioContentThemeList() {
this.is_loading = true
this.is_loading = true;
try {
const res = await api.getAudioContentThemeList()
const res = await api.getAudioContentThemeList();
if (res.status === 200 && res.data.success === true) {
this.themeList = res.data.data.map((item) => {
return {title: item.theme, value: item.id}
})
return { title: item.theme, value: item.id };
});
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(
res.data.message ||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
}
this.is_loading = false
this.is_loading = false;
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.is_loading = false
this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.");
this.is_loading = false;
}
},
async getAudioContent() {
this.is_loading = true
this.is_loading = true;
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) {
const data = res.data.data
const data = res.data.data;
const total_page = Math.ceil(data.totalCount / 10)
this.audio_contents = data.items
const total_page = Math.ceil(data.totalCount / 10);
this.audio_contents = data.items;
if (total_page <= 0)
this.total_page = 1
else
this.total_page = total_page
if (total_page <= 0) this.total_page = 1;
else this.total_page = total_page;
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(
res.data.message ||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
}
this.is_loading = false
this.is_loading = false;
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.is_loading = false
this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.");
this.is_loading = false;
}
},
async search() {
this.page = 1
await this.searchAudioContent()
this.page = 1;
await this.searchAudioContent();
},
async searchAudioContent() {
if (this.search_word.length === 0) {
await this.getAudioContent()
await this.getAudioContent();
} else if (this.search_word.length < 2) {
this.notifyError('검색어를 2글자 이상 입력하세요.')
this.notifyError("검색어를 2글자 이상 입력하세요.");
} else {
this.is_loading = true
this.is_loading = true;
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) {
const data = res.data.data
const data = res.data.data;
const total_page = Math.ceil(data.totalCount / 10)
this.audio_contents = data.items
const total_page = Math.ceil(data.totalCount / 10);
this.audio_contents = data.items;
if (total_page <= 0)
this.total_page = 1
else
this.total_page = total_page
if (total_page <= 0) this.total_page = 1;
else this.total_page = total_page;
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.notifyError(
res.data.message ||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
}
this.is_loading = false
this.is_loading = false;
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.is_loading = false
this.notifyError(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
);
this.is_loading = false;
}
}
},
async shareAudioContent(item) {
this.is_loading = true
this.is_loading = true;
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) {
await navigator.clipboard.writeText(linkData.data.shortLink)
this.notifySuccess("링크가 복사되었습니다.")
await navigator.clipboard.writeText(linkData.data.shortLink);
this.notifySuccess("링크가 복사되었습니다.");
} else {
this.notifyError("링크를 생성하지 못했습니다.")
this.notifyError("링크를 생성하지 못했습니다.");
}
} finally {
this.is_loading = false
this.is_loading = false;
}
},
}
}
},
};
</script>
<style scoped>
</style>
<style scoped></style>