first commit

This commit is contained in:
Yu Sung
2023-08-04 23:02:15 +09:00
commit c60930a566
83 changed files with 38615 additions and 0 deletions

181
src/views/Live/LiveList.vue Normal file
View File

@@ -0,0 +1,181 @@
<template>
<div>
<v-toolbar dark>
<v-spacer />
<v-toolbar-title>라이브 관리 (진행예정 / 진행중)</v-toolbar-title>
<v-spacer />
</v-toolbar>
<br>
<v-container>
<v-row>
<v-col>
<v-data-table
:headers="headers"
:items="liveList"
:loading="isLoading"
:items-per-page="-1"
class="elevation-1"
hide-default-footer
>
<template v-slot:item.id="{ item }">
{{ item.id }}
</template>
<template v-slot:item.title="{ item }">
{{ item.title }}
</template>
<template v-slot:item.content="{ item }">
{{ item.content }}
</template>
<template v-slot:item.nickname="{ item }">
{{ item.managerNickname }}
</template>
<template v-slot:item.cover="{ item }">
<img
:src="item.coverImageUrl"
alt=""
height="100"
width="100"
>
</template>
<template v-slot:item.ing="{ item }">
<span v-if="item.channelName.length > 0">진행중</span>
<span v-else>진행예정</span>
</template>
<template v-slot:item.type="{ item }">
<span v-if="item.type === 'SECRET'">비밀</span>
<span v-else-if="item.type === 'PRIVATE'">비공개</span>
<span v-else>공개</span>
</template>
<template v-slot:item.nickname="{ item }">
{{ item.password }}
</template>
<template v-slot:item.isAdult="{ item }">
<span v-if="item.isAdult">O</span>
<span v-else>X</span>
</template>
</v-data-table>
</v-col>
</v-row>
</v-container>
</div>
</template>
<script>
import * as api from "@/api/live"
export default {
name: "LiveRoom",
data() {
return {
isLoading: false,
liveList: [],
headers: [
{
text: '방번호',
align: 'center',
sortable: false,
value: 'id',
},
{
text: '타이틀',
align: 'center',
sortable: false,
value: 'title',
},
{
text: '공지',
align: 'center',
sortable: false,
value: 'content',
},
{
text: '방장',
align: 'center',
sortable: false,
value: 'managerNickname',
},
{
text: '커버이미지',
align: 'center',
sortable: false,
value: 'cover',
},
{
text: '진행여부',
align: 'center',
sortable: false,
value: 'ing',
},
{
text: '공개여부',
align: 'center',
sortable: false,
value: 'type',
},
{
text: '비밀번호',
align: 'center',
sortable: false,
value: 'password',
},
{
text: '19금',
align: 'center',
sortable: false,
value: 'isAdult',
},
],
}
},
async created() {
await this.getLive()
},
methods: {
notifyError(message) {
this.$dialog.notify.error(message)
},
notifySuccess(message) {
this.$dialog.notify.success(message)
},
async getLive() {
this.isLoading = true
try {
this.isLoading = false
const res = await api.getLive()
if (res.status === 200 && res.data.success === true) {
this.liveList = res.data.data.liveList
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
}
this.isLoading = false
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.isLoading = false
}
}
},
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,537 @@
<template>
<div>
<v-toolbar dark>
<v-spacer />
<v-toolbar-title>추천 요즘라이브</v-toolbar-title>
<v-spacer />
</v-toolbar>
<br>
<v-container>
<v-row>
<v-col cols="10" />
<v-col>
<v-btn
block
color="#9970ff"
dark
depressed
@click="showWriteDialog"
>
추천 요즘라이브 등록
</v-btn>
</v-col>
</v-row>
<v-row>
<v-col>
<v-data-table
:headers="headers"
:items="recommendLiveList"
:loading="isLoading"
:items-per-page="20"
item-key="id"
class="elevation-1"
hide-default-footer
>
<template v-slot:body="props">
<draggable
v-model="props.items"
tag="tbody"
@end="onDropCallback(props.items)"
>
<tr
v-for="(item, index) in props.items"
:key="index"
>
<td>
<img
:src="item.image"
alt=""
height="100"
width="100"
>
</td>
<td> <h3>{{ item.creatorNickname }}</h3> </td>
<td>
<h3>
{{ item.startDate }} ~ {{ item.endDate }}
</h3>
</td>
<td>
<h3 v-if="item.isAdult">
O
</h3>
<h3 v-else>
X
</h3>
</td>
<td>
<v-btn
:disabled="isLoading"
@click="showModifyDialog(item)"
>
수정
</v-btn>
</td>
</tr>
</draggable>
</template>
</v-data-table>
</v-col>
</v-row>
<v-row class="text-center">
<v-col>
<v-pagination
v-model="page"
:length="total_page"
circle
@input="next"
/>
</v-col>
</v-row>
</v-container>
<v-row>
<v-dialog
v-model="show_write_dialog"
max-width="1000px"
persistent
>
<v-card>
<v-card-title>추천 요즘라이브 등록</v-card-title>
<v-card-text>
<v-row align="center">
<v-col cols="4">
요즘친구
</v-col>
<v-col cols="8">
<v-select
v-model="creator_id"
:items="creatorList"
item-text="name"
item-value="value"
label="크리에이터 선택"
/>
</v-col>
</v-row>
</v-card-text>
<v-card-text>
<v-row align="center">
<v-col cols="4">
기간
</v-col>
<v-col
cols="8"
class="datepicker-wrapper"
>
<datetime
v-model="start_date"
class="datepicker"
format="YYYY-MM-DD H:i"
/>
<div> ~ </div>
<datetime
v-model="end_date"
class="datepicker"
format="YYYY-MM-DD H:i"
/>
</v-col>
</v-row>
</v-card-text>
<v-card-text>
<v-row align="center">
<v-col cols="4">
19
</v-col>
<v-col cols="8">
<input
v-model="is_adult"
type="checkbox"
>
</v-col>
</v-row>
</v-card-text>
<v-card-text>
<v-row align="center">
<v-col cols="4">
이미지
</v-col>
<v-col cols="8">
<div class="image-select">
<label for="image">
이미지 불러오기
</label>
<v-file-input
id="image"
v-model="image"
@change="imageAdd"
/>
</div>
<img
v-if="image_url"
:src="image_url"
alt=""
class="image-preview"
>
</v-col>
</v-row>
</v-card-text>
<v-card-actions v-show="!isLoading">
<v-spacer />
<v-btn
color="blue darken-1"
text
@click="cancel"
>
취소
</v-btn>
<v-btn
v-if="selected_recommend_live !== null"
color="blue darken-1"
text
@click="modify"
>
수정
</v-btn>
<v-btn
v-else
color="blue darken-1"
text
@click="validate"
>
등록
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</div>
</template>
<script>
import Draggable from 'vuedraggable';
import datetime from 'vuejs-datetimepicker';
import * as accountApi from "@/api/member";
import * as api from "@/api/recommend_suda_creator"
export default {
name: "LiveRecommendView",
components: { datetime, Draggable },
data() {
return {
isLoading: false,
page: 1,
total_page: 0,
show_write_dialog: false,
image: null,
image_url: null,
creator_id: null,
creatorList: [],
recommendLiveList: [],
selected_recommend_live: null,
start_date: null,
end_date: null,
is_adult: false,
headers: [
{
text: '이미지',
align: 'center',
sortable: false,
value: 'image',
},
{
text: '요즘친구',
align: 'center',
sortable: false,
value: 'creatorNickname',
},
{
text: '기간',
align: 'center',
sortable: false,
value: 'date',
},
{
text: '19금',
align: 'center',
sortable: false,
value: 'isAdult',
},
{
text: '관리',
align: 'center',
sortable: false,
value: 'management'
},
],
}
},
async created() {
await this.getCreatorList()
await this.getRecommendSudaCreator()
},
methods: {
async onDropCallback(items) {
this.recommendLiveList = items
const ids = items.map((item) => {
return item.id
})
const firstOrders = (this.page - 1) * 20 + 1
const res = await api.updateRecommendSudaCreatorOrders(firstOrders, ids)
if (res.status === 200 && res.data.success === true) {
this.notifySuccess(res.data.message)
}
},
showModifyDialog(item) {
this.selected_recommend_live = item;
this.is_adult = item.isAdult;
this.creator_id = item.creatorId;
this.start_date = item.startDate;
this.end_date = item.endDate;
this.image_url = item.image;
this.show_write_dialog = true
},
async modify() {
if (this.is_loading) return;
this.is_loading = true
try {
const formData = new FormData()
formData.append("recommend_suda_creator_id", this.selected_recommend_live.id)
if (this.image !== null) {
formData.append("image", this.image)
}
if (this.selected_recommend_live.creatorId !== this.creator_id) {
formData.append("creator_id", this.creator_id)
}
if (this.selected_recommend_live.startDate !== this.start_date) {
formData.append("start_date", this.start_date)
}
if (this.selected_recommend_live.endDate !== this.end_date) {
formData.append("end_date", this.end_date)
}
if (this.selected_recommend_live.isAdult !== this.is_adult) {
formData.append("is_adult", this.is_adult)
}
const res = await api.updateRecommendSudaCreator(formData)
if (res.status === 200 && res.data.success === true) {
this.cancel()
this.notifySuccess('수정되었습니다.')
this.page = 1
await this.getRecommendSudaCreator()
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.is_loading = false
}
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.is_loading = false
} finally {
this.is_loading = false
}
},
async submit() {
if (this.is_loading) return;
this.isLoading = true
try {
const formData = new FormData()
formData.append("image", this.image)
formData.append("creator_id", this.creator_id)
formData.append("start_date", this.start_date)
formData.append("end_date", this.end_date)
formData.append("is_adult", this.is_adult)
const res = await api.createRecommendSudaCreator(formData);
if (res.status === 200 && res.data.success === true) {
this.cancel()
this.notifySuccess('등록되었습니다.')
this.page = 1
await this.getRecommendSudaCreator()
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.is_loading = false
}
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.is_loading = false
} finally {
this.is_loading = false
}
},
validate() {
if (
this.image === null ||
this.creator_id === null ||
this.start_date === null ||
this.end_date === null
) {
this.notifyError('내용을 입력하세요')
} else {
this.submit()
}
},
cancel() {
this.show_write_dialog = false
this.image = null
this.image_url = null
this.creator_id = null
this.start_date = null
this.end_date = null
},
notifyError(message) {
this.$dialog.notify.error(message)
},
notifySuccess(message) {
this.$dialog.notify.success(message)
},
async next() {
await this.getRecommendSudaCreator()
},
async getRecommendSudaCreator() {
this.isLoading = true
try {
const res = await api.getRecommendSudaCreator(this.page)
if (res.status === 200 && res.data.success === true) {
const data = res.data.data
const totalPage = Math.ceil(data.totalCount / 20)
this.recommendLiveList = data.recommendCreatorList
if (totalPage <= 0)
this.total_page = 1
else
this.total_page = totalPage
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.is_loading = false
}
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.is_loading = false
} finally {
this.isLoading = false
}
},
async getCreatorList() {
this.isLoading = true
try {
const res = await accountApi.getCounselorList()
if (res.status === 200 && res.data.success === true) {
this.creatorList = res.data.data.map((item) => {
return {name: item.nickname, value: item.id}
})
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.is_loading = false
}
} catch (e) {
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
this.is_loading = false
} finally {
this.isLoading = false
}
},
imageAdd(payload) {
const file = payload;
if (file) {
this.image_url = URL.createObjectURL(file)
URL.revokeObjectURL(file)
} else {
this.image_url = null
}
},
showWriteDialog() {
this.show_write_dialog = true
},
}
}
</script>
<style scoped>
.image-select label {
display: inline-block;
padding: 10px 20px;
background-color: #232d4a;
color: #fff;
vertical-align: middle;
font-size: 15px;
cursor: pointer;
border-radius: 5px;
}
.v-file-input {
position: absolute;
width: 0;
height: 0;
padding: 0;
overflow: hidden;
border: 0;
}
.image-preview {
max-width: 100%;
width: 250px;
object-fit: cover;
margin-top: 10px;
}
.datepicker {
text-align: center;
}
.datepicker-wrapper {
display: flex;
flex-direction: row;
}
.datepicker-wrapper > div {
margin: 20px;
}
.v-card__text {
margin-top: 20px;
}
.v-card__actions {
margin-top: 100px;
}
.v-card__actions > .v-btn {
font-size: 20px;
}
td img {
display: block;
margin: 10px auto;
}
</style>

316
src/views/Live/LiveTags.vue Normal file
View File

@@ -0,0 +1,316 @@
<template>
<div>
<v-toolbar dark>
<v-spacer />
<v-toolbar-title>라이브 관심사 관리</v-toolbar-title>
<v-spacer />
</v-toolbar>
<br>
<v-row>
<v-dialog
v-model="show_dialog"
max-width="400px"
persistent
>
<template v-slot:activator="{ on, attrs }">
<v-container>
<v-row>
<v-col cols="10" />
<v-col>
<v-btn
block
color="#9970ff"
dark
depressed
v-bind="attrs"
v-on="on"
>
관심사 추가등록
</v-btn>
</v-col>
</v-row>
<draggable
v-model="tags"
class="row"
@end="onDropCallback(tags)"
>
<v-col
v-for="(tag, i) in tags"
:key="i"
cols="2"
>
<v-card>
<v-card-title>
<v-spacer />
<v-img
:src="tag.image"
class="rounded-circle"
/>
<v-spacer />
</v-card-title>
<v-card-title>
<v-spacer />
{{ tag.tag }}
<v-spacer />
</v-card-title>
<v-card-actions>
<v-spacer />
<v-btn
text
@click="showModifyTagDialog(tag)"
>
수정
</v-btn>
<v-btn
text
@click="deleteTag(tag)"
>
삭제
</v-btn>
<v-spacer />
</v-card-actions>
</v-card>
</v-col>
</draggable>
</v-container>
</template>
<v-card>
<v-card-title>관심사 등록</v-card-title>
<v-card-text>
<v-text-field
v-model="tag_text"
label="관심사"
required
/>
</v-card-text>
<div class="image-select">
<label for="image">
이미지 등록
</label>
<v-file-input
id="image"
v-model="image"
@change="imageAdd"
/>
</div>
<img
v-if="image_url"
:src="image_url"
alt=""
class="image-preview"
>
<v-card-actions v-show="!isLoading">
<v-spacer />
<v-btn
color="blue darken-1"
text
@click="cancel"
>
취소
</v-btn>
<v-btn
v-if="selected_tag !== null"
color="blue darken-1"
text
@click="modify"
>
수정
</v-btn>
<v-btn
v-else
color="blue darken-1"
text
@click="submit"
>
등록
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</div>
</template>
<script>
import * as api from '@/api/live_tag'
import Draggable from "vuedraggable";
export default {
name: "PromotionSudaTags",
components: {Draggable},
data() {
return {
isLoading: false,
tags: [],
show_dialog: false,
selected_tag: null,
tag_text: null,
image: null,
image_url: null
}
},
async created() {
await this.getTags()
},
methods: {
async onDropCallback(items) {
const ids = items.map((item) => {
return item.id
})
const res = await api.updateTagOrders(ids)
if (res.status === 200 && res.data.success === true) {
this.notifySuccess(res.data.message)
}
},
imageAdd(payload) {
const file = payload;
if (file) {
this.image_url = URL.createObjectURL(file)
URL.revokeObjectURL(file)
} else {
this.image_url = null
}
},
notifyError(message) {
this.$dialog.notify.error(message)
},
notifySuccess(message) {
this.$dialog.notify.success(message)
},
cancel() {
this.show_dialog = false
this.image = null
this.image_url = null
this.tag_text = null
},
showModifyTagDialog(tag) {
this.selected_tag = tag
this.image_url = tag.image
this.tag_text = tag.tag
this.show_dialog = true
},
async submit() {
this.isLoading = true
const formData = new FormData()
formData.append("image", this.image)
formData.append("request", JSON.stringify({tag: this.tag_text}))
const res = await api.enrollment(formData)
if (res.status === 200 && res.data.success === true) {
this.show_dialog = false
this.image = null
this.image_url = null
this.tag_text = null
this.tags = []
this.notifySuccess(res.data.message)
await this.getTags()
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
}
this.isLoading = false
},
async getTags() {
this.isLoading = true
try {
let res = await api.getTags();
this.tags = res.data.data
} catch (e) {
this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
} finally {
this.isLoading = false
}
},
async deleteTag(tag) {
this.isLoading = true
try {
let res = await api.deleteTag(tag.id)
if (res.status === 200 && res.data.success === true) {
this.notifySuccess(res.data.message)
await this.getTags()
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
}
} catch (e) {
this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
} finally {
this.isLoading = false
}
},
async modify() {
this.isLoading = true
const formData = new FormData()
formData.append("image", this.image)
formData.append("request", JSON.stringify({tag: this.tag_text}))
const res = await api.modifyTag(this.selected_tag.id, formData)
if (res.status === 200 && res.data.success === true) {
this.show_dialog = false
this.image = null
this.image_url = null
this.tag_text = null
this.tags = []
this.notifySuccess(res.data.message)
await this.getTags()
} else {
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
}
this.isLoading = false
}
}
}
</script>
<style scoped>
.image-select label {
display: inline-block;
padding: 10px 20px;
background-color: #232d4a;
color: #fff;
vertical-align: middle;
font-size: 15px;
cursor: pointer;
border-radius: 5px;
}
.v-file-input {
position: absolute;
width: 0;
height: 0;
padding: 0;
overflow: hidden;
border: 0;
}
.image-preview {
max-width: 100%;
width: 250px;
object-fit: cover;
margin-top: 10px;
}
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>수다친구별 관리</div>
</template>
<script>
export default {
name: "SudaByCounselor"
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>수다현황</div>
</template>
<script>
export default {
name: "SudaStatus"
}
</script>
<style scoped>
</style>