Compare commits
3 Commits
test
...
1def9ddd4a
| Author | SHA1 | Date | |
|---|---|---|---|
| 1def9ddd4a | |||
| a13d442924 | |||
| a1f206a3c0 |
@@ -1,4 +1,2 @@
|
|||||||
VUE_APP_API_URL=https://test-api.sodalive.net
|
VUE_APP_API_URL=https://test-api.sodalive.net
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
VUE_APP_GOOGLE_CLIENT_ID=758414412471-mosodbj2chno7l1j0iihldh6edmk0gk9.apps.googleusercontent.com
|
|
||||||
VUE_APP_KAKAO_JS_KEY=2be3c619ed36fd3e138bf45812c57d7f
|
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
VUE_APP_API_URL=https://api.sodalive.net
|
VUE_APP_API_URL=https://api.sodalive.net
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
VUE_APP_GOOGLE_CLIENT_ID=983594297130-5hrmkh6vpskeq6v34350kmilf74574h2.apps.googleusercontent.com
|
|
||||||
VUE_APP_KAKAO_JS_KEY=378e800dd9029907c559390e786157ef
|
|
||||||
|
|||||||
43
AGENTS.md
43
AGENTS.md
@@ -1,43 +0,0 @@
|
|||||||
# AGENTS.md
|
|
||||||
|
|
||||||
## 문서 목적
|
|
||||||
- 이 문서는 `/Users/klaus/Develop/sodalive/Admin/soda-creator-admin` 저장소에서 작업하는 에이전트용 실행 가이드다.
|
|
||||||
- 목표는 "추측 최소화 + 기존 패턴 준수 + 검증 우선"이다.
|
|
||||||
- 이 문서의 규칙은 코드/테스트/문서 변경 모두에 적용한다.
|
|
||||||
|
|
||||||
## 커뮤니케이션 규칙
|
|
||||||
- **"질문에 대한 답변과 설명은 한국어로 한다."**
|
|
||||||
- 이 저장소에서 사용자에게 전달하는 설명, 진행 상황, 결과 보고는 한국어로 작성한다.
|
|
||||||
- 코드 식별자, 경로, 명령어는 원문(영문) 그대로 유지한다.
|
|
||||||
|
|
||||||
## 커밋 메시지 규칙 (표준 Conventional Commits)
|
|
||||||
- 기본 형식은 `<type>(scope): <description>`를 사용한다.
|
|
||||||
- `type`은 소문자(`feat`, `fix`, `chore`, `docs`, `refactor`, `test` 등)를 사용한다.
|
|
||||||
- 제목(description)은 한글로 작성하고, 명령형/간결한 현재형으로 작성한다.
|
|
||||||
|
|
||||||
### 커밋 메시지 검증 절차
|
|
||||||
- `git commit` 실행 직전에 `work/check-commit-message-rules.sh`를 실행해 규칙 준수 여부를 확인한다.
|
|
||||||
- `git commit` 실행 직후에도 `work/check-commit-message-rules.sh`를 다시 실행해 최종 메시지를 재검증한다.
|
|
||||||
- 스크립트 결과가 `[FAIL]`이면 커밋 메시지를 규칙에 맞게 수정한 뒤 다시 검증한다.
|
|
||||||
|
|
||||||
## 작업 계획 문서 규칙 (docs)
|
|
||||||
- 모든 작업 시작 전에 `docs` 폴더 아래에 계획 문서를 먼저 생성하고, 해당 문서를 기준으로 구현을 진행한다.
|
|
||||||
- **기능 추가 중 발생한 오류 수정이나 보완 작업은 새로운 문서를 만들지 않고, 기존 기능 추가 계획 문서에 내용을 추가한다.**
|
|
||||||
- 계획 문서 파일명은 `[날짜]_구현할내용한글.md` 형식을 사용한다.
|
|
||||||
- 날짜는 `YYYYMMDD` 8자리 숫자를 사용한다.
|
|
||||||
- 파일명 예시: `20260101_구글계정으로로그인.md`
|
|
||||||
- 구현 항목은 기능/작업 단위로 분리해 체크박스(`- [ ]`) 목록으로 작성한다.
|
|
||||||
- 구현 완료 시마다 체크박스를 `- [x]`로 갱신하고, 각 항목이 정상 구현되었는지 확인한다.
|
|
||||||
- 작업 도중 범위가 변경되면 계획 문서의 체크박스 항목을 먼저 업데이트한 뒤 구현을 진행한다.
|
|
||||||
- 모든 구현이 끝난 후 결과 보고 시 계획 문서 맨 아래에 무엇을, 왜, 어떻게 검증했는지 한국어로 간단히 기록한다.
|
|
||||||
- 후속 수정이 발생해도 기존 검증 기록은 삭제/덮어쓰지 않고 누적한다(예: `1차 구현`, `2차 수정`).
|
|
||||||
- 검증 기록은 단계별로 `무엇을/왜/어떻게`를 유지해 작성하고, 이전 단계와 구분이 되도록 명시한다.
|
|
||||||
- 단계별 `어떻게`에는 실제 실행한 검증 명령과 결과(성공/실패/불가 사유)를 함께 기록한다.
|
|
||||||
- 기존 기록 정정이 필요하면 원문을 지우지 말고 `정정` 항목을 추가해 사유와 변경 내용을 남긴다.
|
|
||||||
|
|
||||||
## 에이전트 동작 원칙
|
|
||||||
- 추측하지 말고, 근거 파일을 읽고 결정한다.
|
|
||||||
- 기존 관례를 깨는 변경은 이유가 명확할 때만 수행한다.
|
|
||||||
- 불필요한 리팩터링 확장은 피하고 요청 범위를 우선 충족한다.
|
|
||||||
- 결과 보고 시 무엇을, 왜, 어떻게 검증했는지 한국어로 간단히 남긴다.
|
|
||||||
-
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# 20260227 시그니처 이미지 크롭 기능 추가
|
|
||||||
|
|
||||||
## 구현 목표
|
|
||||||
시그니처 이미지를 등록할 때 사용자 편의를 위해 크롭 기능을 추가한다.
|
|
||||||
- 해상도 가로 최대 800px (800px보다 작은 경우 원본 크기 유지)
|
|
||||||
- 비율 1:1 지원
|
|
||||||
- GIF 이미지는 크롭 없이 바로 업로드
|
|
||||||
- 기타 이미지 포맷은 크롭 기능 적용
|
|
||||||
|
|
||||||
## 작업 내역
|
|
||||||
- [x] `SignatureManagement.vue` 수정
|
|
||||||
- [x] `cropperjs` 임포트 및 스타일 추가
|
|
||||||
- [x] 이미지 크롭 다이얼로그 UI 구현
|
|
||||||
- [x] `imageAdd` 메서드 수정 (GIF 구분 및 크롭 처리)
|
|
||||||
- [x] `cropImage` 메서드 구현 (1:1 비율 및 800px 리사이징 로직 포함)
|
|
||||||
- [x] `cancelCropper` 메서드 구현
|
|
||||||
- [x] `data`에 크롭 관련 변수 추가
|
|
||||||
- [x] CSS 스타일 정의
|
|
||||||
|
|
||||||
## 검증 계획
|
|
||||||
- [x] GIF 이미지 업로드 테스트: 크롭 다이얼로그 없이 바로 등록되는지 확인 (코드 로직 검증)
|
|
||||||
- [x] 일반 이미지(JPG/PNG) 업로드 테스트: 1:1 비율 크롭 다이얼로그 표시 확인 (코드 로직 검증)
|
|
||||||
- [x] 이미지 리사이징 테스트:
|
|
||||||
- [x] 가로 800px 초과 이미지 크롭 시 가로 800px로 리사이징되는지 확인 (코드 로직 검증)
|
|
||||||
- [x] 가로 800px 이하 이미지 크롭 시 원본 가로 크기가 유지되는지 확인 (코드 로직 검증)
|
|
||||||
- [x] 크롭 후 최종 등록 및 서버 업로드 확인 (코드 로직 검증)
|
|
||||||
|
|
||||||
## 검증 기록
|
|
||||||
### 1차 구현
|
|
||||||
- 무엇을: 시그니처 이미지 등록 시 1:1 크롭 기능 구현
|
|
||||||
- 왜: 시그니처 이미지 해상도 관리 및 사용자 편의 제공
|
|
||||||
- 어떻게:
|
|
||||||
- `cropperjs`를 사용하여 1:1 비율 크롭 다이얼로그 구현
|
|
||||||
- GIF 포맷은 `file.type === 'image/gif'` 조건으로 크롭 제외
|
|
||||||
- `getCroppedCanvas()` 이후 가로가 800px을 초과할 경우 `canvas`를 사용하여 800x800으로 리사이징 수행
|
|
||||||
- 800px 이하인 경우 원본 크롭 해상도 유지
|
|
||||||
- `SignatureManagement.vue` 파일 수정 및 관련 스타일 추가
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# 20260227_시리즈커버이미지크롭기능추가.md
|
|
||||||
|
|
||||||
## 구현할 내용
|
|
||||||
- [x] `ContentSeriesList.vue`에 `cropperjs`를 활용한 이미지 크롭 기능 추가
|
|
||||||
- [x] 1:1.5 비율(aspectRatio: 1/1.5) 지원
|
|
||||||
- [x] 가로 해상도 최대 1000px 제한 로직 추가 (1000px 미만은 원본 크기 유지)
|
|
||||||
- [x] 크롭 다이얼로그 및 관련 상태 관리 로직 구현
|
|
||||||
- [x] 크롭 후 `_isCropped` 플래그를 통한 중복 처리 방지
|
|
||||||
- [x] 크롭 다이얼로그 내 이미지 표시 영역 높이 제한 및 스타일 수정
|
|
||||||
- [x] 큰 이미지가 표시될 때 다이얼로그 밖으로 나가지 않도록 `max-height` 조정
|
|
||||||
- [x] 시리즈 이미지 크롭 비율을 A4 비율(210:297)로 수정
|
|
||||||
|
|
||||||
## 검증 기록
|
|
||||||
### 1차 구현
|
|
||||||
- **무엇을**: `ContentSeriesList.vue` 시리즈 등록/수정 시 커버 이미지 크롭 기능 추가
|
|
||||||
- **왜**: 시리즈 커버 이미지의 비율을 1:1.5로 강제하고, 고해상도 이미지 업로드 시 가로 1000px로 리사이징하여 서버 부하 및 레이아웃 깨짐을 방지하기 위함
|
|
||||||
- **어떻게**:
|
|
||||||
- `cropperjs` 라이브러리를 도입하여 `aspectRatio: 1 / 1.5` 설정
|
|
||||||
- `cropImage` 메서드에서 `canvas.width`가 1000px을 초과할 경우에만 리사이징 수행
|
|
||||||
- `file._isCropped` 플래그를 사용하여 크롭 완료 후 `v-file-input` 모델 변경 시 발생하는 `imageAdd` 중복 호출 방지
|
|
||||||
- `npm run lint`를 통해 코드 스타일 검증 완료
|
|
||||||
|
|
||||||
### 2차 수정
|
|
||||||
- **무엇을**: `ContentSeriesList.vue`의 크롭퍼 이미지 컨테이너 스타일 수정
|
|
||||||
- **왜**: 이미지가 너무 클 경우 다이얼로그를 벗어나 스크롤을 해야만 '크롭 완료' 버튼을 누를 수 있는 불편함이 있음.
|
|
||||||
- **어떻게**:
|
|
||||||
- `.cropper-wrapper`의 `max-height`를 `500px`에서 `70vh`로 변경하여 브라우저 높이에 대응하도록 수정.
|
|
||||||
- `npm run lint` 실행 결과 이상 없음 확인.
|
|
||||||
+
|
|
||||||
+### 3차 수정
|
|
||||||
+- **무엇을**: 시리즈 커버 이미지 크롭 비율 수정 (1:1.5 -> A4 비율)
|
|
||||||
+- **왜**: 시리즈 이미지 규격을 A4 비율(210:297)로 통일하기 위함
|
|
||||||
+- **어떻게**:
|
|
||||||
+ - `ContentSeriesList.vue`에서 `aspectRatio`를 `210 / 297`로 수정
|
|
||||||
+ - 크롭 다이얼로그 타이틀을 "이미지 크롭 (A4 비율)"으로 변경
|
|
||||||
+ - 리사이징 시 높이 계산 방식을 `MAX_WIDTH * (297 / 210)`으로 수정
|
|
||||||
+ - 목록의 커버 이미지 `aspect-ratio`를 `210/297`로 수정
|
|
||||||
+ - `npm run lint` 실행 결과 이상 없음 확인
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# 20260227_이미지크롭포커스이슈수정.md
|
|
||||||
|
|
||||||
## 구현할 내용
|
|
||||||
- [x] 이미지 크롭 후 포커스가 다른 곳으로 가면 다시 크롭화면이 표시되는 문제 해결
|
|
||||||
- [x] 크롭 후 `v-file-input` 값 초기화 로직 추가
|
|
||||||
|
|
||||||
## 검증 기록
|
|
||||||
### 1차 수정
|
|
||||||
- **무엇을**: `ContentList.vue`의 이미지 선택 및 크롭 로직 수정
|
|
||||||
- **왜**: `v-file-input`에 선택된 파일이 남아 있어 다른 곳에 포커스가 갔다가 돌아오거나 입력이 발생할 때 `@change` 이벤트가 예기치 않게 발생할 가능성이 있고, 사용자 경험상 불편함이 있음.
|
|
||||||
- **어떻게**:
|
|
||||||
- `imageAdd` 메서드에서 파일을 처리하기 전에 `file._isCropped` 플래그를 확인하여 이미 크롭된 파일인 경우 중복 처리를 방지함.
|
|
||||||
- `cropImage` 메서드에서 크롭된 새로운 `File` 객체를 생성할 때 `_isCropped = true` 플래그를 설정하여 `audio_content.cover_image`가 업데이트될 때 발생하는 `@change` 이벤트에 의해 크롭 다이얼로그가 다시 열리는 것을 방지함.
|
|
||||||
- `npm run lint` 실행 결과 이상 없음.
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# 20260227_초기환경설정및가이드준수
|
|
||||||
|
|
||||||
## 구현 항목
|
|
||||||
- [x] AGENTS.md 파일 확인 및 내용 숙지
|
|
||||||
- [x] work/check-commit-message-rules.sh 파일 확인 및 실행 권한 확인
|
|
||||||
- [x] 변경 사항 스테이징
|
|
||||||
- [x] 커밋 메시지 규칙 검증
|
|
||||||
- [x] 가이드에 따른 커밋 수행
|
|
||||||
|
|
||||||
## 검증 기록
|
|
||||||
### 1차 구현
|
|
||||||
#### 무엇을
|
|
||||||
- 초기 프로젝트 설정 파일(`AGENTS.md`, `work/check-commit-message-rules.sh`)을 커밋한다.
|
|
||||||
#### 왜
|
|
||||||
- 에이전트 작업 가이드를 준수하고, 이후 작업의 기반을 마련하기 위함이다.
|
|
||||||
#### 어떻게
|
|
||||||
- `work/check-commit-message-rules.sh --message "docs: 에이전트 작업 가이드 및 검증 스크립트 추가"` 명령으로 메시지 규칙을 검증했다.
|
|
||||||
- `git commit -m "docs: 에이전트 작업 가이드 및 검증 스크립트 추가"` 명령으로 커밋을 시도한다.
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# 20260227_콘텐츠커버이미지크롭기능추가.md
|
|
||||||
|
|
||||||
## 작업 개요
|
|
||||||
콘텐츠 등록 및 수정 시 커버 이미지를 1:1 비율로 크롭하여 등록할 수 있는 기능을 추가한다.
|
|
||||||
|
|
||||||
## 작업 항목
|
|
||||||
- [x] cropperjs 라이브러리 설치
|
|
||||||
- [x] ContentList.vue에 크롭용 다이얼로그 및 UI 추가
|
|
||||||
- [x] cropperjs 연동 및 1:1 비율 설정 구현
|
|
||||||
- [x] 이미지 리사이징 로직 구현 (800x800 제한)
|
|
||||||
- [x] 콘텐츠 등록(save) 및 수정(modify) 로직에 크롭된 이미지 적용
|
|
||||||
- [x] 기존 이미지 선택 로직(imageAdd) 수정
|
|
||||||
|
|
||||||
## 검증 계획
|
|
||||||
### 무엇을
|
|
||||||
- 커버 이미지 선택 시 크롭 다이얼로그가 정상적으로 표시되는지 확인
|
|
||||||
- 1:1 비율로 크롭이 제한되는지 확인
|
|
||||||
- 해상도가 800px 이상인 이미지가 800x800으로 리사이징되는지 확인
|
|
||||||
- 해상도가 800px 미만인 이미지가 원래 해상도를 유지하며 1:1로 크롭되는지 확인
|
|
||||||
- 최종적으로 서버에 크롭된 이미지가 정상 전달되는지 확인
|
|
||||||
|
|
||||||
### 왜
|
|
||||||
- 사용자 요구사항(1:1 비율, 800x800 제한) 충족 여부 확인
|
|
||||||
|
|
||||||
### 결과 보고
|
|
||||||
1차 구현 완료.
|
|
||||||
- 무엇을: 커버 이미지 크롭 기능 및 800x800 리사이징 로직 구현
|
|
||||||
- 왜: 1:1 비율 유지 및 서버 저장 용량 최적화를 위해
|
|
||||||
- 어떻게:
|
|
||||||
- `npm install cropperjs` 실행
|
|
||||||
- `ContentList.vue`에 `cropperjs` 연동 및 `canvas`를 이용한 리사이징 로직 추가
|
|
||||||
- `npm run lint`를 통해 코드 스타일 검증 (성공)
|
|
||||||
|
|
||||||
2차 수정 완료.
|
|
||||||
- 무엇을: `Module not found: Error: Can't resolve 'cropperjs/dist/cropper.css'` 에러 수정
|
|
||||||
- 왜: `cropperjs` 2.x 버전에서는 CSS 파일이 포함되어 있지 않아 빌드 에러 발생
|
|
||||||
- 어떻게:
|
|
||||||
- `cropperjs`를 1.x 버전(`^1.5.13`)으로 재설치하여 CSS 파일을 포함하도록 수정
|
|
||||||
- `npm run lint`를 통해 코드 스타일 검증 (성공)
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# 20260326_시그니처이미지크롭비율자유조정
|
|
||||||
|
|
||||||
## 구현 항목
|
|
||||||
- [x] `src/views/Signature/SignatureManagement.vue` 수정
|
|
||||||
- [x] `Cropper` 초기화 시 `aspectRatio: 1`을 제거하여 자유 비율 허용
|
|
||||||
- [x] UI 상의 "(1:1 비율)" 안내 텍스트 수정
|
|
||||||
- [x] `cropImage` 메서드 내 리사이징 로직 수정 (비율 유지하며 리사이징)
|
|
||||||
|
|
||||||
## 검증 기록
|
|
||||||
- 1차 구현
|
|
||||||
- 무엇을: 시그니처 이미지 크롭 비율 자유 조정 기능
|
|
||||||
- 왜: 사용자가 1:1 비율이 아닌 자유로운 비율로 이미지를 등록할 수 있도록 하기 위함
|
|
||||||
- 어떻게:
|
|
||||||
- `src/views/Signature/SignatureManagement.vue` 파일의 `Cropper` 설정에서 `aspectRatio: 1`을 제거하고, `cropImage` 메서드에서 리사이징 시 고정 높이가 아닌 원본 비율에 따른 높이를 계산하도록 수정함.
|
|
||||||
- `work/check-commit-message-rules.sh`를 사용하여 커밋 메시지 규칙 준수 여부 확인 완료.
|
|
||||||
75
package-lock.json
generated
75
package-lock.json
generated
@@ -9,13 +9,9 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"cropperjs": "^1.6.2",
|
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-router": "^3.2.0",
|
"vue-router": "^3.2.0",
|
||||||
"vue-show-more-text": "^2.0.2",
|
|
||||||
"vue2-datepicker": "^3.11.1",
|
|
||||||
"vue2-editor": "^2.10.3",
|
"vue2-editor": "^2.10.3",
|
||||||
"vue2-timepicker": "^1.1.6",
|
|
||||||
"vuedraggable": "^2.24.3",
|
"vuedraggable": "^2.24.3",
|
||||||
"vuejs-datetimepicker": "^1.1.13",
|
"vuejs-datetimepicker": "^1.1.13",
|
||||||
"vuetify": "2.6.10",
|
"vuetify": "2.6.10",
|
||||||
@@ -4840,11 +4836,6 @@
|
|||||||
"sha.js": "^2.4.8"
|
"sha.js": "^2.4.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cropperjs": {
|
|
||||||
"version": "1.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.6.2.tgz",
|
|
||||||
"integrity": "sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA=="
|
|
||||||
},
|
|
||||||
"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",
|
||||||
@@ -5180,11 +5171,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
|
||||||
"integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw=="
|
"integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw=="
|
||||||
},
|
},
|
||||||
"node_modules/date-format-parse": {
|
|
||||||
"version": "0.2.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/date-format-parse/-/date-format-parse-0.2.7.tgz",
|
|
||||||
"integrity": "sha512-/+lyMUKoRogMuTeOVii6lUwjbVlesN9YRYLzZT/g3TEZ3uD9QnpjResujeEqUW+OSNbT7T1+SYdyEkTcRv+KDQ=="
|
|
||||||
},
|
|
||||||
"node_modules/de-indent": {
|
"node_modules/de-indent": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||||
@@ -14495,15 +14481,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.3.tgz",
|
||||||
"integrity": "sha512-FUlILrW3DGitS2h+Xaw8aRNvGTwtuaxrRkNSHWTizOfLUie7wuYwezeZ50iflRn8YPV5kxmU2LQuu3nM/b3Zsg=="
|
"integrity": "sha512-FUlILrW3DGitS2h+Xaw8aRNvGTwtuaxrRkNSHWTizOfLUie7wuYwezeZ50iflRn8YPV5kxmU2LQuu3nM/b3Zsg=="
|
||||||
},
|
},
|
||||||
"node_modules/vue-show-more-text": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue-show-more-text/-/vue-show-more-text-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-x/WuikWAx8Hm4gpZx6KHtJYiXDordGdSoXrd34lTiJeAnlT8Y7Yc0FfGBNdUv6mXncuET3LiRwwNz+X5gI+oiw==",
|
|
||||||
"dependencies": {
|
|
||||||
"core-js": "^3.6.5",
|
|
||||||
"vue": "^2.6.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vue-style-loader": {
|
"node_modules/vue-style-loader": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
||||||
@@ -14536,17 +14513,6 @@
|
|||||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/vue2-datepicker": {
|
|
||||||
"version": "3.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue2-datepicker/-/vue2-datepicker-3.11.1.tgz",
|
|
||||||
"integrity": "sha512-6PU/+pnp2mgZAfnSXmbdwj9516XsEvTiw61Q5SNrvvdy8W/FCxk1GAe9UZn/m9YfS5A47yK6XkcjMHbp7aFApA==",
|
|
||||||
"dependencies": {
|
|
||||||
"date-format-parse": "^0.2.7"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"vue": "^2.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vue2-editor": {
|
"node_modules/vue2-editor": {
|
||||||
"version": "2.10.3",
|
"version": "2.10.3",
|
||||||
"resolved": "https://registry.npmjs.org/vue2-editor/-/vue2-editor-2.10.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue2-editor/-/vue2-editor-2.10.3.tgz",
|
||||||
@@ -14555,14 +14521,6 @@
|
|||||||
"quill": "^1.3.6"
|
"quill": "^1.3.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue2-timepicker": {
|
|
||||||
"version": "1.1.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue2-timepicker/-/vue2-timepicker-1.1.6.tgz",
|
|
||||||
"integrity": "sha512-DhO2UH4CTer/lMWxg+jqxn/+h6g2vZrsM6vCe9u7/Ie+Pej9yA+8mQA3C3VPApZ+LauKc43WxCspOXb6SGBOTw==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"vue": "^2.6.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vuedraggable": {
|
"node_modules/vuedraggable": {
|
||||||
"version": "2.24.3",
|
"version": "2.24.3",
|
||||||
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
|
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
|
||||||
@@ -19528,11 +19486,6 @@
|
|||||||
"sha.js": "^2.4.8"
|
"sha.js": "^2.4.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cropperjs": {
|
|
||||||
"version": "1.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.6.2.tgz",
|
|
||||||
"integrity": "sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA=="
|
|
||||||
},
|
|
||||||
"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",
|
||||||
@@ -19797,11 +19750,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
|
||||||
"integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw=="
|
"integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw=="
|
||||||
},
|
},
|
||||||
"date-format-parse": {
|
|
||||||
"version": "0.2.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/date-format-parse/-/date-format-parse-0.2.7.tgz",
|
|
||||||
"integrity": "sha512-/+lyMUKoRogMuTeOVii6lUwjbVlesN9YRYLzZT/g3TEZ3uD9QnpjResujeEqUW+OSNbT7T1+SYdyEkTcRv+KDQ=="
|
|
||||||
},
|
|
||||||
"de-indent": {
|
"de-indent": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||||
@@ -27273,15 +27221,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.3.tgz",
|
||||||
"integrity": "sha512-FUlILrW3DGitS2h+Xaw8aRNvGTwtuaxrRkNSHWTizOfLUie7wuYwezeZ50iflRn8YPV5kxmU2LQuu3nM/b3Zsg=="
|
"integrity": "sha512-FUlILrW3DGitS2h+Xaw8aRNvGTwtuaxrRkNSHWTizOfLUie7wuYwezeZ50iflRn8YPV5kxmU2LQuu3nM/b3Zsg=="
|
||||||
},
|
},
|
||||||
"vue-show-more-text": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue-show-more-text/-/vue-show-more-text-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-x/WuikWAx8Hm4gpZx6KHtJYiXDordGdSoXrd34lTiJeAnlT8Y7Yc0FfGBNdUv6mXncuET3LiRwwNz+X5gI+oiw==",
|
|
||||||
"requires": {
|
|
||||||
"core-js": "^3.6.5",
|
|
||||||
"vue": "^2.6.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"vue-style-loader": {
|
"vue-style-loader": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
||||||
@@ -27316,14 +27255,6 @@
|
|||||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"vue2-datepicker": {
|
|
||||||
"version": "3.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue2-datepicker/-/vue2-datepicker-3.11.1.tgz",
|
|
||||||
"integrity": "sha512-6PU/+pnp2mgZAfnSXmbdwj9516XsEvTiw61Q5SNrvvdy8W/FCxk1GAe9UZn/m9YfS5A47yK6XkcjMHbp7aFApA==",
|
|
||||||
"requires": {
|
|
||||||
"date-format-parse": "^0.2.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"vue2-editor": {
|
"vue2-editor": {
|
||||||
"version": "2.10.3",
|
"version": "2.10.3",
|
||||||
"resolved": "https://registry.npmjs.org/vue2-editor/-/vue2-editor-2.10.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue2-editor/-/vue2-editor-2.10.3.tgz",
|
||||||
@@ -27332,12 +27263,6 @@
|
|||||||
"quill": "^1.3.6"
|
"quill": "^1.3.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vue2-timepicker": {
|
|
||||||
"version": "1.1.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue2-timepicker/-/vue2-timepicker-1.1.6.tgz",
|
|
||||||
"integrity": "sha512-DhO2UH4CTer/lMWxg+jqxn/+h6g2vZrsM6vCe9u7/Ie+Pej9yA+8mQA3C3VPApZ+LauKc43WxCspOXb6SGBOTw==",
|
|
||||||
"requires": {}
|
|
||||||
},
|
|
||||||
"vuedraggable": {
|
"vuedraggable": {
|
||||||
"version": "2.24.3",
|
"version": "2.24.3",
|
||||||
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
|
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
|
||||||
|
|||||||
@@ -11,13 +11,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"cropperjs": "^1.6.2",
|
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-router": "^3.2.0",
|
"vue-router": "^3.2.0",
|
||||||
"vue-show-more-text": "^2.0.2",
|
|
||||||
"vue2-datepicker": "^3.11.1",
|
|
||||||
"vue2-editor": "^2.10.3",
|
"vue2-editor": "^2.10.3",
|
||||||
"vue2-timepicker": "^1.1.6",
|
|
||||||
"vuedraggable": "^2.24.3",
|
"vuedraggable": "^2.24.3",
|
||||||
"vuejs-datetimepicker": "^1.1.13",
|
"vuejs-datetimepicker": "^1.1.13",
|
||||||
"vuetify": "2.6.10",
|
"vuetify": "2.6.10",
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 4.2 KiB |
@@ -5,11 +5,9 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
<title>보이스온 크리에이터 관리자</title>
|
<title>소다라이브</title>
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
|
||||||
<script src="https://accounts.google.com/gsi/client" async defer></script>
|
|
||||||
<script src="https://developers.kakao.com/sdk/js/kakao.min.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
|
|||||||
11
src/App.vue
11
src/App.vue
@@ -7,7 +7,7 @@
|
|||||||
dark
|
dark
|
||||||
>
|
>
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
<v-toolbar-title>보이스온 크리에이터 관리자</v-toolbar-title>
|
<v-toolbar-title>소다라이브 크리에이터</v-toolbar-title>
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
|
||||||
@@ -38,13 +38,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.noscroll {
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noscroll::-webkit-scrollbar {
|
|
||||||
overflow-y: hidden;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
async function getGenreList() {
|
|
||||||
return Vue.axios.get("/creator-admin/audio-content/series/genre");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSeriesList(page) {
|
|
||||||
return Vue.axios.get("/creator-admin/audio-content/series?page=" + (page - 1) + "&size=20");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createSeries(formData) {
|
|
||||||
return Vue.axios.post("/creator-admin/audio-content/series", formData, {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "multipart/form-data",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function modifySeries(formData) {
|
|
||||||
return Vue.axios.put("/creator-admin/audio-content/series", formData, {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "multipart/form-data",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSeriesDetail(seriesId) {
|
|
||||||
return Vue.axios.get("/creator-admin/audio-content/series/" + seriesId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSeriesContent(seriesId, page) {
|
|
||||||
return Vue.axios.get(
|
|
||||||
"/creator-admin/audio-content/series/" + seriesId + "/content?page=" + (page - 1) + "&size=10"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function searchContentNotInSeries(seriesId, searchWord) {
|
|
||||||
return Vue.axios.get(
|
|
||||||
"/creator-admin/audio-content/series/content/search?series_id=" + seriesId +
|
|
||||||
"&search_word=" + searchWord
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addingContentToTheSeries(seriesId, contentIdList) {
|
|
||||||
return Vue.axios.post(
|
|
||||||
"/creator-admin/audio-content/series/add/content",
|
|
||||||
{seriesId: seriesId, contentIdList: contentIdList}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeContentInTheSeries(seriesId, contentId) {
|
|
||||||
return Vue.axios.put(
|
|
||||||
"/creator-admin/audio-content/series/remove/content",
|
|
||||||
{seriesId: seriesId, contentId: contentId}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateSeriesOrders(ids) {
|
|
||||||
return Vue.axios.put(
|
|
||||||
"/creator-admin/audio-content/series/orders",
|
|
||||||
{ids: ids}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
getGenreList,
|
|
||||||
getSeriesList,
|
|
||||||
createSeries,
|
|
||||||
modifySeries,
|
|
||||||
getSeriesDetail,
|
|
||||||
getSeriesContent,
|
|
||||||
updateSeriesOrders,
|
|
||||||
addingContentToTheSeries,
|
|
||||||
removeContentInTheSeries,
|
|
||||||
searchContentNotInSeries
|
|
||||||
}
|
|
||||||
@@ -4,43 +4,4 @@ async function getCalculateLive(startDate, endDate) {
|
|||||||
return Vue.axios.get('/creator-admin/calculate/live?startDateStr=' + startDate + '&endDateStr=' + endDate);
|
return Vue.axios.get('/creator-admin/calculate/live?startDateStr=' + startDate + '&endDateStr=' + endDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCalculateContent(startDate, endDate, page, size) {
|
export { getCalculateLive }
|
||||||
return Vue.axios.get(
|
|
||||||
'/creator-admin/calculate/content-list?startDateStr=' +
|
|
||||||
startDate + '&endDateStr=' + endDate + "&page=" + (page - 1) + "&size=" + size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCumulativeSalesByContent(page, size) {
|
|
||||||
return Vue.axios.get('/creator-admin/calculate/cumulative-sales-by-content?page=' + (page - 1) + "&size=" + size);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCalculateContentDonation(startDate, endDate, page, size) {
|
|
||||||
return Vue.axios.get(
|
|
||||||
'/creator-admin/calculate/content-donation-list?startDateStr=' +
|
|
||||||
startDate + '&endDateStr=' + endDate + "&page=" + (page - 1) + "&size=" + size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCalculateCommunityPost(startDate, endDate, page, size) {
|
|
||||||
return Vue.axios.get(
|
|
||||||
'/creator-admin/calculate/community-post?startDateStr=' +
|
|
||||||
startDate + '&endDateStr=' + endDate + "&page=" + (page - 1) + "&size=" + size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCalculateChannelDonation(startDate, endDate, page, size) {
|
|
||||||
return Vue.axios.get(
|
|
||||||
'/creator-admin/calculate/channel-donation?startDateStr=' +
|
|
||||||
startDate + '&endDateStr=' + endDate + "&page=" + (page - 1) + "&size=" + size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
getCalculateLive,
|
|
||||||
getCalculateContent,
|
|
||||||
getCumulativeSalesByContent,
|
|
||||||
getCalculateContentDonation,
|
|
||||||
getCalculateCommunityPost,
|
|
||||||
getCalculateChannelDonation
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
async function saveCategory(title) {
|
|
||||||
return Vue.axios.post('/category', {title: title, contentIdList: []});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function modifyCategory(categoryId, title = null, addContentIdList = [], removeContentIdList = []) {
|
|
||||||
return Vue.axios.put('/category', {
|
|
||||||
categoryId: categoryId,
|
|
||||||
title: title,
|
|
||||||
addContentIdList: addContentIdList,
|
|
||||||
removeContentIdList: removeContentIdList
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteCategory(categoryId) {
|
|
||||||
return Vue.axios.delete('/category/' + categoryId)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCategoryList(creatorId) {
|
|
||||||
return Vue.axios.get('/category?creatorId=' + creatorId)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateCategoryOrders(ids) {
|
|
||||||
return Vue.axios.put('/category/orders', {ids: ids})
|
|
||||||
}
|
|
||||||
|
|
||||||
export {saveCategory, modifyCategory, deleteCategory, getCategoryList, updateCategoryOrders}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import Vue from "vue";
|
|
||||||
|
|
||||||
async function getContentInCategory(categoryId, page) {
|
|
||||||
return Vue.axios.get(
|
|
||||||
"/creator-admin/content-category?category_id=" + categoryId +
|
|
||||||
"&page=" + (page - 1) +
|
|
||||||
"&size=10"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function searchContentNotInCategory(categoryId, searchWord) {
|
|
||||||
return Vue.axios.get(
|
|
||||||
"/creator-admin/content-category/search?category_id=" + categoryId +
|
|
||||||
"&search_word=" + searchWord
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export {getContentInCategory, searchContentNotInCategory}
|
|
||||||
@@ -8,28 +8,4 @@ async function logout() {
|
|||||||
return Vue.axios.post('/creator-admin/member/logout');
|
return Vue.axios.post('/creator-admin/member/logout');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loginGoogle(idToken) {
|
export { login, logout }
|
||||||
return Vue.axios.post(
|
|
||||||
"/member/login/google",
|
|
||||||
{ container: "api" },
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${idToken}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loginKakao(accessToken) {
|
|
||||||
return Vue.axios.post(
|
|
||||||
"/member/login/kakao",
|
|
||||||
{ container: "api" },
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${accessToken}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export { login, logout, loginGoogle, loginKakao }
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
async function getSignatureList(page, sort) {
|
|
||||||
return Vue.axios.get('/creator-admin/signature?page=' + (page - 1) + "&size=20" + "&sort-type=" + sort);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createSignature(formData) {
|
|
||||||
return Vue.axios.post('/creator-admin/signature', formData, {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "multipart/form-data",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function modifySignature(formData) {
|
|
||||||
return Vue.axios.put('/creator-admin/signature', formData, {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "multipart/form-data",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export { getSignatureList, createSignature, modifySignature }
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.9 KiB |
@@ -20,10 +20,11 @@
|
|||||||
:to="item.route"
|
:to="item.route"
|
||||||
active-class="blue white--text"
|
active-class="blue white--text"
|
||||||
>
|
>
|
||||||
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
|
||||||
<v-list-item-icon>
|
<v-list-item-icon>
|
||||||
<v-icon>mdi-chevron-right</v-icon>
|
<v-icon>{{ item.icon }}</v-icon>
|
||||||
</v-list-item-icon>
|
</v-list-item-icon>
|
||||||
|
|
||||||
|
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -20,56 +20,11 @@ const routes = [
|
|||||||
name: 'ContentList',
|
name: 'ContentList',
|
||||||
component: () => import(/* webpackChunkName: "content" */ '../views/Content/ContentList.vue')
|
component: () => import(/* webpackChunkName: "content" */ '../views/Content/ContentList.vue')
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/content/category/list',
|
|
||||||
name: 'ContentCategoryList',
|
|
||||||
component: () => import(/* webpackChunkName: "content" */ '../views/Content/ContentCategoryList.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/content/series/list',
|
|
||||||
name: 'ContentSeriesList',
|
|
||||||
component: () => import(/* webpackChunkName: "content" */ '../views/Content/ContentSeriesList.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/content/series/detail',
|
|
||||||
name: 'ContentSeriesDetail',
|
|
||||||
component: () => import(/* webpackChunkName: "content" */ '../views/Content/ContentSeriesDetail.vue')
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/calculate/live',
|
path: '/calculate/live',
|
||||||
name: 'CalculateLive',
|
name: 'CalculateLive',
|
||||||
component: () => import(/* webpackChunkName: "calculate" */ '../views/Calculate/CalculateLive.vue')
|
component: () => import(/* webpackChunkName: "calculate" */ '../views/Calculate/CalculateLive.vue')
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/calculate/content-by-date',
|
|
||||||
name: 'CalculateContent',
|
|
||||||
component: () => import(/* webpackChunkName: "calculate" */ '../views/Calculate/CalculateContent.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/calculate/content-accumulation',
|
|
||||||
name: 'CalculateAccumulation',
|
|
||||||
component: () => import(/* webpackChunkName: "calculate" */ '../views/Calculate/CalculateAccumulation.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/calculate/content-donation-by-date',
|
|
||||||
name: 'CalculateContentDonation',
|
|
||||||
component: () => import(/* webpackChunkName: "calculate" */ '../views/Calculate/CalculateContentDonation.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/calculate/community-post',
|
|
||||||
name: 'CalculateCommunityPost',
|
|
||||||
component: () => import(/* webpackChunkName: "calculate" */ '../views/Calculate/CalculateCommunityPost.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/calculate/channel-donation',
|
|
||||||
name: 'CalculateChannelDonation',
|
|
||||||
component: () => import(/* webpackChunkName: "calculate" */ '../views/Calculate/CalculateChannelDonation.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/signature',
|
|
||||||
name: 'SignatureManagement',
|
|
||||||
component: () => import(/* webpackChunkName: "signature" */ '../views/Signature/SignatureManagement.vue')
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -87,12 +42,6 @@ const router = new VueRouter({
|
|||||||
|
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
if (to.path !== '/login') {
|
if (to.path !== '/login') {
|
||||||
if (to.path === '/content/series/list') {
|
|
||||||
document.documentElement.classList.add('noscroll'); // html에 클래스 추가
|
|
||||||
} else {
|
|
||||||
document.documentElement.classList.remove('noscroll');
|
|
||||||
}
|
|
||||||
|
|
||||||
const isAuthenticated = store.getters['accountStore/isAuthenticated']
|
const isAuthenticated = store.getters['accountStore/isAuthenticated']
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -87,56 +87,6 @@ const accountStore = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async LOGIN_GOOGLE({commit}, {idToken}) {
|
|
||||||
let result = false
|
|
||||||
let errorMessage = null
|
|
||||||
|
|
||||||
try {
|
|
||||||
let res = await memberApi.loginGoogle(idToken)
|
|
||||||
if (res.data.success === true) {
|
|
||||||
commit("LOGIN", res.data.data)
|
|
||||||
result = true
|
|
||||||
} else {
|
|
||||||
errorMessage = res.data.message
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
errorMessage = '구글 로그인 정보를 확인해주세요.'
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (result) {
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject(errorMessage)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async LOGIN_KAKAO({commit}, {accessToken}) {
|
|
||||||
let result = false
|
|
||||||
let errorMessage = null
|
|
||||||
|
|
||||||
try {
|
|
||||||
let res = await memberApi.loginKakao(accessToken)
|
|
||||||
if (res.data.success === true) {
|
|
||||||
commit("LOGIN", res.data.data)
|
|
||||||
result = true
|
|
||||||
} else {
|
|
||||||
errorMessage = res.data.message
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
errorMessage = '카카오 로그인 정보를 확인해주세요.'
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (result) {
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject(errorMessage)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async LOGOUT({commit}) {
|
async LOGOUT({commit}) {
|
||||||
let result = false
|
let result = false
|
||||||
let errorMessage = null
|
let errorMessage = null
|
||||||
|
|||||||
@@ -1,209 +0,0 @@
|
|||||||
<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="items"
|
|
||||||
:loading="is_loading"
|
|
||||||
:items-per-page="-1"
|
|
||||||
class="elevation-1"
|
|
||||||
hide-default-footer
|
|
||||||
>
|
|
||||||
<template v-slot:item.orderPrice="{ item }">
|
|
||||||
{{ item.orderPrice.toLocaleString() }} 캔
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.totalCan="{ item }">
|
|
||||||
{{ item.totalCan.toLocaleString() }} 캔
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.totalKrw="{ item }">
|
|
||||||
{{ item.totalKrw.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.paymentFee="{ item }">
|
|
||||||
{{ item.paymentFee.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.settlementAmount="{ item }">
|
|
||||||
{{ item.settlementAmount.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.tax="{ item }">
|
|
||||||
{{ item.tax.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.depositAmount="{ item }">
|
|
||||||
{{ item.depositAmount.toLocaleString() }} 원
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as api from "@/api/calculate";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "CalculateAccumulation",
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
is_loading: false,
|
|
||||||
page: 1,
|
|
||||||
page_size: 20,
|
|
||||||
total_page: 0,
|
|
||||||
items: [],
|
|
||||||
headers: [
|
|
||||||
{
|
|
||||||
text: '크리에이터',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'nickname',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '등록일',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'registrationDate',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '제목',
|
|
||||||
sortable: false,
|
|
||||||
value: 'title',
|
|
||||||
align: 'center',
|
|
||||||
width: "300px"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '구분',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'orderType',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '판매금액(캔)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'orderPrice',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '누적 판매수',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'numberOfPeople',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '합계(캔)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'totalCan',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '원화',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'totalKrw',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '수수료\n(6.6%)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'paymentFee',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '정산금액',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'settlementAmount',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '원천세\n(3.3%)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'tax',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '입금액',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'depositAmount',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
await this.getCumulativeSalesByContent();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
notifyError(message) {
|
|
||||||
this.$dialog.notify.error(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
notifySuccess(message) {
|
|
||||||
this.$dialog.notify.success(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
sumField(key) {
|
|
||||||
return this.items.reduce((a, b) => a + (b[key] || 0), 0)
|
|
||||||
},
|
|
||||||
|
|
||||||
async next() {
|
|
||||||
await this.getCumulativeSalesByContent()
|
|
||||||
},
|
|
||||||
|
|
||||||
async getCumulativeSalesByContent() {
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await api.getCumulativeSalesByContent(this.page, this.page_size)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
const data = res.data.data
|
|
||||||
|
|
||||||
const totalPage = Math.ceil(data.totalCount / this.page_size)
|
|
||||||
this.items = data.items
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -1,281 +0,0 @@
|
|||||||
<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="2">
|
|
||||||
<datetime
|
|
||||||
v-model="start_date"
|
|
||||||
class="datepicker"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="1">
|
|
||||||
~
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="2">
|
|
||||||
<datetime
|
|
||||||
v-model="end_date"
|
|
||||||
class="datepicker"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="1" />
|
|
||||||
|
|
||||||
<v-col cols="2">
|
|
||||||
<v-btn
|
|
||||||
block
|
|
||||||
color="#3bb9f1"
|
|
||||||
dark
|
|
||||||
depressed
|
|
||||||
@click="getCalculateChannelDonation"
|
|
||||||
>
|
|
||||||
조회
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-spacer />
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
<v-data-table
|
|
||||||
:headers="headers"
|
|
||||||
:items="items"
|
|
||||||
:loading="is_loading"
|
|
||||||
:items-per-page="-1"
|
|
||||||
class="elevation-1"
|
|
||||||
hide-default-footer
|
|
||||||
>
|
|
||||||
<template slot="body.prepend">
|
|
||||||
<tr v-if="total">
|
|
||||||
<td colspan="2">
|
|
||||||
합계
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
{{ total.count.toLocaleString() }} 건
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
{{ total.totalCan.toLocaleString() }} 캔
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
{{ total.krw.toLocaleString() }} 원
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
{{ total.fee.toLocaleString() }} 원
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
{{ total.settlementAmount.toLocaleString() }} 원
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
{{ total.withholdingTax.toLocaleString() }} 원
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
{{ total.depositAmount.toLocaleString() }} 원
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.count="{ item }">
|
|
||||||
{{ item.count.toLocaleString() }} 건
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.totalCan="{ item }">
|
|
||||||
{{ item.totalCan.toLocaleString() }} 캔
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.krw="{ item }">
|
|
||||||
{{ item.krw.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.fee="{ item }">
|
|
||||||
{{ item.fee.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.settlementAmount="{ item }">
|
|
||||||
{{ item.settlementAmount.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.withholdingTax="{ item }">
|
|
||||||
{{ item.withholdingTax.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.depositAmount="{ item }">
|
|
||||||
{{ item.depositAmount.toLocaleString() }} 원
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as api from "@/api/calculate";
|
|
||||||
import datetime from "vuejs-datetimepicker";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "CalculateChannelDonation",
|
|
||||||
components: {datetime},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
is_loading: false,
|
|
||||||
start_date: null,
|
|
||||||
end_date: null,
|
|
||||||
page: 1,
|
|
||||||
page_size: 20,
|
|
||||||
total_page: 0,
|
|
||||||
items: [],
|
|
||||||
total: null,
|
|
||||||
headers: [
|
|
||||||
{
|
|
||||||
text: '날짜',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'date',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '크리에이터',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'creator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '건수',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'count',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '캔',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'totalCan',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '원화',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'krw',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '수수료\n(6.6%)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'fee',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '정산금액',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'settlementAmount',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '원천세\n(3.3%)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'withholdingTax',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '입금액',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'depositAmount',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
const date = new Date();
|
|
||||||
const firstDate = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
||||||
const lastDate = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
||||||
|
|
||||||
let firstDateMonth = (firstDate.getMonth() + 1).toString()
|
|
||||||
if (firstDateMonth.length < 2) {
|
|
||||||
firstDateMonth = '0' + firstDateMonth
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastDateMonth = (lastDate.getMonth() + 1).toString()
|
|
||||||
if (lastDateMonth.length < 2) {
|
|
||||||
lastDateMonth = '0' + lastDateMonth
|
|
||||||
}
|
|
||||||
|
|
||||||
let firstDateDay = firstDate.getDate().toString()
|
|
||||||
if (firstDateDay.length < 2) {
|
|
||||||
firstDateDay = '0' + firstDateDay
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastDateDay = lastDate.getDate().toString()
|
|
||||||
if (lastDateDay.length < 2) {
|
|
||||||
lastDateDay = '0' + lastDateDay
|
|
||||||
}
|
|
||||||
|
|
||||||
this.start_date = firstDate.getFullYear() + '-' + firstDateMonth + '-' + firstDateDay
|
|
||||||
this.end_date = lastDate.getFullYear() + '-' + lastDateMonth + '-' + lastDateDay
|
|
||||||
|
|
||||||
await this.getCalculateChannelDonation()
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
notifyError(message) {
|
|
||||||
this.$dialog.notify.error(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
notifySuccess(message) {
|
|
||||||
this.$dialog.notify.success(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
async next() {
|
|
||||||
await this.getCalculateChannelDonation()
|
|
||||||
},
|
|
||||||
|
|
||||||
async getCalculateChannelDonation() {
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await api.getCalculateChannelDonation(this.start_date, this.end_date, this.page, this.page_size)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.items = res.data.data.items
|
|
||||||
this.total = res.data.data.total
|
|
||||||
this.total_page = Math.ceil(res.data.data.totalCount / this.page_size)
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.datepicker {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
<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="2">
|
|
||||||
<datetime
|
|
||||||
v-model="start_date"
|
|
||||||
class="datepicker"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="1">
|
|
||||||
~
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="2">
|
|
||||||
<datetime
|
|
||||||
v-model="end_date"
|
|
||||||
class="datepicker"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="1" />
|
|
||||||
|
|
||||||
<v-col cols="2">
|
|
||||||
<v-btn
|
|
||||||
block
|
|
||||||
color="#9970ff"
|
|
||||||
dark
|
|
||||||
depressed
|
|
||||||
@click="getCalculateCommunityPost"
|
|
||||||
>
|
|
||||||
조회
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
<v-spacer />
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
<v-data-table
|
|
||||||
:headers="headers"
|
|
||||||
:items="items"
|
|
||||||
:loading="is_loading"
|
|
||||||
:items-per-page="-1"
|
|
||||||
class="elevation-1"
|
|
||||||
hide-default-footer
|
|
||||||
>
|
|
||||||
<template slot="body.prepend">
|
|
||||||
<tr>
|
|
||||||
<td colspan="4">
|
|
||||||
합계
|
|
||||||
</td>
|
|
||||||
<td>{{ sumField('totalCan').toLocaleString() }} 캔</td>
|
|
||||||
<td>{{ sumField('totalKrw').toLocaleString() }} 원</td>
|
|
||||||
<td>{{ sumField('paymentFee').toLocaleString() }} 원</td>
|
|
||||||
<td>{{ sumField('settlementAmount').toLocaleString() }} 원</td>
|
|
||||||
<td>{{ sumField('tax').toLocaleString() }} 원</td>
|
|
||||||
<td>{{ sumField('depositAmount').toLocaleString() }} 원</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.title="{ item }">
|
|
||||||
{{ item.title }}...
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.totalCan="{ item }">
|
|
||||||
{{ item.totalCan.toLocaleString() }} 캔
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.totalKrw="{ item }">
|
|
||||||
{{ item.totalKrw.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.paymentFee="{ item }">
|
|
||||||
{{ item.paymentFee.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.settlementAmount="{ item }">
|
|
||||||
{{ item.settlementAmount.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.tax="{ item }">
|
|
||||||
{{ item.tax.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.depositAmount="{ item }">
|
|
||||||
{{ item.depositAmount.toLocaleString() }} 원
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import datetime from "vuejs-datetimepicker";
|
|
||||||
import * as api from "@/api/calculate";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "CalculateCommunityPost",
|
|
||||||
components: {datetime},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
is_loading: false,
|
|
||||||
start_date: null,
|
|
||||||
end_date: null,
|
|
||||||
page: 1,
|
|
||||||
page_size: 20,
|
|
||||||
total_page: 0,
|
|
||||||
items: [],
|
|
||||||
headers: [
|
|
||||||
{
|
|
||||||
text: '날짜',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'date',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '내용(앞 10글자)',
|
|
||||||
sortable: false,
|
|
||||||
value: 'title',
|
|
||||||
align: 'center',
|
|
||||||
width: "300px"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '판매금액(캔)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'can',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '구매유저수',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'numberOfPurchase',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '합계(캔)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'totalCan',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '원화',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'totalKrw',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '수수료\n(6.6%)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'paymentFee',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '정산금액',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'settlementAmount',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '원천세\n(3.3%)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'tax',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '입금액',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'depositAmount',
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
const date = new Date();
|
|
||||||
const firstDate = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
||||||
const lastDate = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
||||||
|
|
||||||
let firstDateMonth = (firstDate.getMonth() + 1).toString()
|
|
||||||
if (firstDateMonth.length < 2) {
|
|
||||||
firstDateMonth = '0' + firstDateMonth
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let lastDateMonth = (lastDate.getMonth() + 1).toString()
|
|
||||||
if (lastDateMonth.length < 2) {
|
|
||||||
lastDateMonth = '0' + lastDateMonth
|
|
||||||
}
|
|
||||||
|
|
||||||
this.start_date = firstDate.getFullYear() + '-' + firstDateMonth + '-0' + firstDate.getDate()
|
|
||||||
this.end_date = lastDate.getFullYear() + '-' + lastDateMonth + '-' + lastDate.getDate()
|
|
||||||
|
|
||||||
await this.getCalculateCommunityPost();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
notifyError(message) {
|
|
||||||
this.$dialog.notify.error(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
sumField(key) {
|
|
||||||
return this.items.reduce((a, b) => a + (b[key] || 0), 0)
|
|
||||||
},
|
|
||||||
|
|
||||||
async next() {
|
|
||||||
await this.getCalculateCommunityPost()
|
|
||||||
},
|
|
||||||
|
|
||||||
async getCalculateCommunityPost() {
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await api.getCalculateCommunityPost(this.start_date, this.end_date, this.page, this.page_size)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
const data = res.data.data
|
|
||||||
|
|
||||||
const totalPage = Math.ceil(data.totalCount / this.page_size)
|
|
||||||
this.items = data.items
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
<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="2">
|
|
||||||
<datetime
|
|
||||||
v-model="start_date"
|
|
||||||
class="datepicker"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="1">
|
|
||||||
~
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="2">
|
|
||||||
<datetime
|
|
||||||
v-model="end_date"
|
|
||||||
class="datepicker"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="1" />
|
|
||||||
|
|
||||||
<v-col cols="2">
|
|
||||||
<v-btn
|
|
||||||
block
|
|
||||||
color="#9970ff"
|
|
||||||
dark
|
|
||||||
depressed
|
|
||||||
@click="getCalculateContent"
|
|
||||||
>
|
|
||||||
조회
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
|
|
||||||
<v-spacer />
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
<v-data-table
|
|
||||||
:headers="headers"
|
|
||||||
:items="items"
|
|
||||||
:loading="is_loading"
|
|
||||||
:items-per-page="-1"
|
|
||||||
class="elevation-1"
|
|
||||||
hide-default-footer
|
|
||||||
>
|
|
||||||
<template slot="body.prepend">
|
|
||||||
<tr>
|
|
||||||
<td colspan="5">
|
|
||||||
합계
|
|
||||||
</td>
|
|
||||||
<td>{{ sumField('numberOfPeople').toLocaleString() }}</td>
|
|
||||||
<td>{{ sumField('totalCan').toLocaleString() }} 캔</td>
|
|
||||||
<td>{{ sumField('totalKrw').toLocaleString() }} 원</td>
|
|
||||||
<td>{{ sumField('paymentFee').toLocaleString() }} 원</td>
|
|
||||||
<td>{{ sumField('settlementAmount').toLocaleString() }} 원</td>
|
|
||||||
<td>{{ sumField('tax').toLocaleString() }} 원</td>
|
|
||||||
<td>{{ sumField('depositAmount').toLocaleString() }} 원</td>
|
|
||||||
<td />
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.orderPrice="{ item }">
|
|
||||||
{{ item.orderPrice.toLocaleString() }} 캔
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.orderType="{ item }">
|
|
||||||
{{ item.orderType }}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.totalCan="{ item }">
|
|
||||||
{{ item.totalCan.toLocaleString() }} 캔
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.totalKrw="{ item }">
|
|
||||||
{{ item.totalKrw.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.paymentFee="{ item }">
|
|
||||||
{{ item.paymentFee.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.settlementAmount="{ item }">
|
|
||||||
{{ item.settlementAmount.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.tax="{ item }">
|
|
||||||
{{ item.tax.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.depositAmount="{ item }">
|
|
||||||
{{ item.depositAmount.toLocaleString() }} 원
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as api from "@/api/calculate";
|
|
||||||
import datetime from 'vuejs-datetimepicker';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "CalculateContent",
|
|
||||||
components: {datetime},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
is_loading: false,
|
|
||||||
start_date: null,
|
|
||||||
end_date: null,
|
|
||||||
page: 1,
|
|
||||||
page_size: 20,
|
|
||||||
total_page: 0,
|
|
||||||
items: [],
|
|
||||||
headers: [
|
|
||||||
{
|
|
||||||
text: '판매일',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'saleDate',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '크리에이터',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'nickname',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '제목',
|
|
||||||
sortable: false,
|
|
||||||
value: 'title',
|
|
||||||
align: 'center',
|
|
||||||
width: "300px"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '구분',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'orderType',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '판매금액(캔)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'orderPrice',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '판매수',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'numberOfPeople',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '합계(캔)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'totalCan',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '원화',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'totalKrw',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '수수료\n(6.6%)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'paymentFee',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '정산금액',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'settlementAmount',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '원천세\n(3.3%)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'tax',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '입금액',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'depositAmount',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '등록일',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'registrationDate',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
const date = new Date();
|
|
||||||
const firstDate = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
||||||
const lastDate = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
||||||
|
|
||||||
let firstDateMonth = (firstDate.getMonth() + 1).toString()
|
|
||||||
if (firstDateMonth.length < 2) {
|
|
||||||
firstDateMonth = '0' + firstDateMonth
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let lastDateMonth = (lastDate.getMonth() + 1).toString()
|
|
||||||
if (lastDateMonth.length < 2) {
|
|
||||||
lastDateMonth = '0' + lastDateMonth
|
|
||||||
}
|
|
||||||
|
|
||||||
this.start_date = firstDate.getFullYear() + '-' + firstDateMonth + '-0' + firstDate.getDate()
|
|
||||||
this.end_date = lastDate.getFullYear() + '-' + lastDateMonth + '-' + lastDate.getDate()
|
|
||||||
|
|
||||||
await this.getCalculateContent();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
notifyError(message) {
|
|
||||||
this.$dialog.notify.error(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
notifySuccess(message) {
|
|
||||||
this.$dialog.notify.success(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
sumField(key) {
|
|
||||||
return this.items.reduce((a, b) => a + (b[key] || 0), 0)
|
|
||||||
},
|
|
||||||
|
|
||||||
async next() {
|
|
||||||
await this.getCalculateContent()
|
|
||||||
},
|
|
||||||
|
|
||||||
async getCalculateContent() {
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await api.getCalculateContent(this.start_date, this.end_date, this.page, this.page_size)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
const data = res.data.data
|
|
||||||
|
|
||||||
const totalPage = Math.ceil(data.totalCount / this.page_size)
|
|
||||||
this.items = data.items
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
<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="2">
|
|
||||||
<datetime
|
|
||||||
v-model="start_date"
|
|
||||||
class="datepicker"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="1">
|
|
||||||
~
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="2">
|
|
||||||
<datetime
|
|
||||||
v-model="end_date"
|
|
||||||
class="datepicker"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="1" />
|
|
||||||
|
|
||||||
<v-col cols="2">
|
|
||||||
<v-btn
|
|
||||||
block
|
|
||||||
color="#9970ff"
|
|
||||||
dark
|
|
||||||
depressed
|
|
||||||
@click="getCalculateContentDonation"
|
|
||||||
>
|
|
||||||
조회
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
|
|
||||||
<v-spacer />
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
<v-data-table
|
|
||||||
:headers="headers"
|
|
||||||
:items="items"
|
|
||||||
:loading="is_loading"
|
|
||||||
:items-per-page="-1"
|
|
||||||
class="elevation-1"
|
|
||||||
hide-default-footer
|
|
||||||
>
|
|
||||||
<template v-slot:item.totalCan="{ item }">
|
|
||||||
{{ item.totalCan.toLocaleString() }} 캔
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.totalKrw="{ item }">
|
|
||||||
{{ item.totalKrw.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.paymentFee="{ item }">
|
|
||||||
{{ item.paymentFee.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.settlementAmount="{ item }">
|
|
||||||
{{ item.settlementAmount.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.tax="{ item }">
|
|
||||||
{{ item.tax.toLocaleString() }} 원
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.depositAmount="{ item }">
|
|
||||||
{{ item.depositAmount.toLocaleString() }} 원
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as api from "@/api/calculate";
|
|
||||||
import datetime from "vuejs-datetimepicker";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "CalculateContentDonation",
|
|
||||||
components: {datetime},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
is_loading: false,
|
|
||||||
start_date: null,
|
|
||||||
end_date: null,
|
|
||||||
page: 1,
|
|
||||||
page_size: 20,
|
|
||||||
total_page: 0,
|
|
||||||
items: [],
|
|
||||||
headers: [
|
|
||||||
{
|
|
||||||
text: '후원날짜',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'donationDate',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '크리에이터',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'nickname',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '콘텐츠 제목',
|
|
||||||
sortable: false,
|
|
||||||
value: 'title',
|
|
||||||
align: 'center',
|
|
||||||
width: "300px"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '구분',
|
|
||||||
sortable: false,
|
|
||||||
value: 'paidOrFree',
|
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '후원수',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'numberOfDonation',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '합계(캔)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'totalCan',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '원화',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'totalKrw',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '수수료\n(6.6%)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'paymentFee',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '정산금액',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'settlementAmount',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '원천세\n(3.3%)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'tax',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '입금액',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'depositAmount',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '콘텐츠 등록일',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'registrationDate',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
const date = new Date();
|
|
||||||
const firstDate = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
||||||
const lastDate = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
||||||
|
|
||||||
let firstDateMonth = (firstDate.getMonth() + 1).toString()
|
|
||||||
if (firstDateMonth.length < 2) {
|
|
||||||
firstDateMonth = '0' + firstDateMonth
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let lastDateMonth = (lastDate.getMonth() + 1).toString()
|
|
||||||
if (lastDateMonth.length < 2) {
|
|
||||||
lastDateMonth = '0' + lastDateMonth
|
|
||||||
}
|
|
||||||
|
|
||||||
this.start_date = firstDate.getFullYear() + '-' + firstDateMonth + '-0' + firstDate.getDate()
|
|
||||||
this.end_date = lastDate.getFullYear() + '-' + lastDateMonth + '-' + lastDate.getDate()
|
|
||||||
|
|
||||||
await this.getCalculateContentDonation();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
notifyError(message) {
|
|
||||||
this.$dialog.notify.error(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
notifySuccess(message) {
|
|
||||||
this.$dialog.notify.success(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
sumField(key) {
|
|
||||||
return this.items.reduce((a, b) => a + (b[key] || 0), 0)
|
|
||||||
},
|
|
||||||
|
|
||||||
async next() {
|
|
||||||
await this.getCalculateContentDonation()
|
|
||||||
},
|
|
||||||
|
|
||||||
async getCalculateContentDonation() {
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await api.getCalculateContentDonation(this.start_date, this.end_date, this.page, this.page_size)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
const data = res.data.data
|
|
||||||
|
|
||||||
const totalPage = Math.ceil(data.totalCount / this.page_size)
|
|
||||||
this.items = data.items
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,650 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<v-container>
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="3">
|
|
||||||
<v-btn
|
|
||||||
block
|
|
||||||
color="#3bb9f1"
|
|
||||||
dark
|
|
||||||
depressed
|
|
||||||
@click="showCreateCategoryDialog"
|
|
||||||
>
|
|
||||||
카테고리 추가
|
|
||||||
</v-btn>
|
|
||||||
<br><br>
|
|
||||||
<draggable
|
|
||||||
:list="category_list"
|
|
||||||
class="list-group"
|
|
||||||
@end="onDropCategoryCallback()"
|
|
||||||
>
|
|
||||||
<v-list
|
|
||||||
v-for="category in category_list"
|
|
||||||
:key="category.category"
|
|
||||||
class="py-0"
|
|
||||||
rounded
|
|
||||||
>
|
|
||||||
<v-list-item :class="{ 'category-selected-item': category === selected_category }">
|
|
||||||
<v-list-item-title @click="clickCategory(category)">
|
|
||||||
{{ category.category }}
|
|
||||||
</v-list-item-title>
|
|
||||||
<v-list-item-icon @click="clickModifyCategory(category)">
|
|
||||||
<v-icon>mdi-pencil</v-icon>
|
|
||||||
</v-list-item-icon>
|
|
||||||
</v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</draggable>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="1" />
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="9" />
|
|
||||||
<v-col cols="3">
|
|
||||||
<v-btn
|
|
||||||
block
|
|
||||||
color="#3bb9f1"
|
|
||||||
dark
|
|
||||||
depressed
|
|
||||||
@click="showAddContent"
|
|
||||||
>
|
|
||||||
콘텐츠 추가
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
<br><br>
|
|
||||||
<p v-if="selected_category !== null && selected_category !== undefined">
|
|
||||||
선택된 카테고리 : {{ selected_category.category }}
|
|
||||||
</p>
|
|
||||||
<p v-else>
|
|
||||||
카테고리를 선택해 주세요
|
|
||||||
</p>
|
|
||||||
<v-simple-table>
|
|
||||||
<template>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="text-center">
|
|
||||||
썸네일
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
제목
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
19금
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
관리
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="category-content-table-tbody">
|
|
||||||
<tr
|
|
||||||
v-for="content in content_list"
|
|
||||||
:key="content.contentId"
|
|
||||||
class="category-content-table-tr"
|
|
||||||
>
|
|
||||||
<td align="center">
|
|
||||||
<v-img
|
|
||||||
max-width="100"
|
|
||||||
max-height="100"
|
|
||||||
:src="content.image"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
|
||||||
{{ content.title }}
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
|
||||||
<div v-if="content.isAdult">
|
|
||||||
O
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
X
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
|
||||||
<v-btn
|
|
||||||
color="#3bb9f1"
|
|
||||||
@click="removeContentInCategory(content)"
|
|
||||||
>
|
|
||||||
삭제
|
|
||||||
</v-btn>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</template>
|
|
||||||
</v-simple-table>
|
|
||||||
<br><br>
|
|
||||||
<v-row
|
|
||||||
v-if="total_page > 0"
|
|
||||||
class="text-center"
|
|
||||||
>
|
|
||||||
<v-col>
|
|
||||||
<v-pagination
|
|
||||||
v-model="page"
|
|
||||||
:length="total_page"
|
|
||||||
circle
|
|
||||||
@input="next"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
|
|
||||||
<v-container>
|
|
||||||
<v-row>
|
|
||||||
<v-dialog
|
|
||||||
v-model="show_create_category_dialog"
|
|
||||||
max-width="1000px"
|
|
||||||
persistent
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-title v-if="modify_category">
|
|
||||||
카테고리 수정
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-title v-else>
|
|
||||||
카테고리 추가
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-text>
|
|
||||||
<v-text-field
|
|
||||||
v-model="title"
|
|
||||||
label="카테고리 제목"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions v-show="!is_loading">
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="cancelCreateCategory"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
v-if="modify_category"
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="deleteCategory"
|
|
||||||
>
|
|
||||||
삭제
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
v-if="modify_category"
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="modifyCategory"
|
|
||||||
>
|
|
||||||
수정
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
v-else
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="saveCategory"
|
|
||||||
>
|
|
||||||
등록
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
|
|
||||||
<v-container>
|
|
||||||
<v-row>
|
|
||||||
<v-dialog
|
|
||||||
v-model="show_add_content_dialog"
|
|
||||||
max-width="1000px"
|
|
||||||
persistent
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-title>
|
|
||||||
콘텐츠 추가 -
|
|
||||||
<span v-if="selected_category !== null">
|
|
||||||
선택된 카테고리 : {{ selected_category.category }}
|
|
||||||
</span>
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-text>
|
|
||||||
<v-text-field
|
|
||||||
v-model="search_word"
|
|
||||||
label="콘텐츠 제목"
|
|
||||||
@keyup.enter="searchContentNotInCategory"
|
|
||||||
>
|
|
||||||
<v-btn
|
|
||||||
slot="append"
|
|
||||||
color="#3bb9f1"
|
|
||||||
dark
|
|
||||||
@click="searchContentNotInCategory"
|
|
||||||
>
|
|
||||||
검색
|
|
||||||
</v-btn>
|
|
||||||
</v-text-field>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text v-if="search_content_list.length > 0 || add_content_list.length > 0">
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
검색결과
|
|
||||||
<v-simple-table>
|
|
||||||
<template v-slot:default>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="text-center">
|
|
||||||
제목
|
|
||||||
</th>
|
|
||||||
<th />
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="content in search_content_list"
|
|
||||||
:key="content.contentId"
|
|
||||||
>
|
|
||||||
<td>{{ content.title }}</td>
|
|
||||||
<td>
|
|
||||||
<v-btn
|
|
||||||
color="#3bb9f1"
|
|
||||||
@click="addContent(content)"
|
|
||||||
>
|
|
||||||
추가
|
|
||||||
</v-btn>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</template>
|
|
||||||
</v-simple-table>
|
|
||||||
</v-col>
|
|
||||||
<v-col v-if="add_content_list.length > 0">
|
|
||||||
추가할 콘텐츠
|
|
||||||
<v-simple-table>
|
|
||||||
<template>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="text-center">
|
|
||||||
제목
|
|
||||||
</th>
|
|
||||||
<th />
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="content in add_content_list"
|
|
||||||
:key="content.contentId"
|
|
||||||
>
|
|
||||||
<td>{{ content.title }}</td>
|
|
||||||
<td>
|
|
||||||
<v-btn
|
|
||||||
color="#3bb9f1"
|
|
||||||
@click="removeContent(content)"
|
|
||||||
>
|
|
||||||
제거
|
|
||||||
</v-btn>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</template>
|
|
||||||
</v-simple-table>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions v-show="!is_loading">
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="cancelAddContent"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="addContentInCategory"
|
|
||||||
>
|
|
||||||
추가
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Draggable from 'vuedraggable';
|
|
||||||
|
|
||||||
import * as api from "@/api/category";
|
|
||||||
import * as contentCategoryApi from "@/api/content_category";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "ContentCategoryList",
|
|
||||||
components: {Draggable},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
is_loading: false,
|
|
||||||
show_add_content_dialog: false,
|
|
||||||
show_create_category_dialog: false,
|
|
||||||
modify_category: false,
|
|
||||||
|
|
||||||
title: '',
|
|
||||||
selected_category: null,
|
|
||||||
modify_selected_category: null,
|
|
||||||
|
|
||||||
search_word: '',
|
|
||||||
search_content_list: [],
|
|
||||||
add_content_list: [],
|
|
||||||
|
|
||||||
category_list: [],
|
|
||||||
content_list: [],
|
|
||||||
|
|
||||||
page: 1,
|
|
||||||
total_page: 0,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
await this.getCategoryList()
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
notifyError(message) {
|
|
||||||
this.$dialog.notify.error(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
notifySuccess(message) {
|
|
||||||
this.$dialog.notify.success(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
showCreateCategoryDialog() {
|
|
||||||
if (this.category_list.length >= 10) {
|
|
||||||
this.notifyError('카테고리는 최대 10개 까지 등록 가능합니다.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.show_create_category_dialog = true
|
|
||||||
},
|
|
||||||
|
|
||||||
showAddContent() {
|
|
||||||
if (this.selected_category === null || this.selected_category === undefined) {
|
|
||||||
this.notifyError('카테고리를 선택하세요')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.show_add_content_dialog = true
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelCreateCategory() {
|
|
||||||
this.title = ''
|
|
||||||
this.modify_category = false
|
|
||||||
this.modify_selected_category = null
|
|
||||||
this.show_create_category_dialog = false
|
|
||||||
},
|
|
||||||
|
|
||||||
clickModifyCategory(category) {
|
|
||||||
this.modify_category = true
|
|
||||||
this.modify_selected_category = category
|
|
||||||
|
|
||||||
this.title = category.category
|
|
||||||
this.show_create_category_dialog = true
|
|
||||||
},
|
|
||||||
|
|
||||||
addContent(content) {
|
|
||||||
this.search_content_list = this.search_content_list.filter((item) => {
|
|
||||||
return item.contentId !== content.contentId
|
|
||||||
});
|
|
||||||
this.add_content_list.push(content)
|
|
||||||
},
|
|
||||||
|
|
||||||
removeContent(content) {
|
|
||||||
this.add_content_list = this.add_content_list.filter((item) => {
|
|
||||||
return item.contentId !== content.contentId
|
|
||||||
});
|
|
||||||
this.search_content_list.push(content)
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelAddContent() {
|
|
||||||
this.search_word = ''
|
|
||||||
this.search_content_list = []
|
|
||||||
this.add_content_list = []
|
|
||||||
this.show_add_content_dialog = false
|
|
||||||
},
|
|
||||||
|
|
||||||
async removeContentInCategory(content) {
|
|
||||||
this.is_loading = true
|
|
||||||
const contentIdList = [content.contentId]
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await api.modifyCategory(
|
|
||||||
this.selected_category.categoryId,
|
|
||||||
null,
|
|
||||||
[],
|
|
||||||
contentIdList,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.cancelCreateCategory()
|
|
||||||
this.cancelAddContent()
|
|
||||||
this.page = 1
|
|
||||||
this.total_page = 0
|
|
||||||
this.notifySuccess('삭제되었습니다.')
|
|
||||||
await this.getContentInCategory()
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async addContentInCategory() {
|
|
||||||
this.is_loading = true
|
|
||||||
const contentIdList = this.add_content_list.map((item) => {
|
|
||||||
return item.contentId
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await api.modifyCategory(
|
|
||||||
this.selected_category.categoryId,
|
|
||||||
null,
|
|
||||||
contentIdList,
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.cancelCreateCategory()
|
|
||||||
this.cancelAddContent()
|
|
||||||
this.page = 1
|
|
||||||
this.total_page = 0
|
|
||||||
await this.getContentInCategory()
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async getCategoryList() {
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
let res = await api.getCategoryList(this.$store.state.accountStore.userId)
|
|
||||||
if (res.data.success === true) {
|
|
||||||
this.category_list = res.data.data
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async saveCategory() {
|
|
||||||
if (this.title.trim().length <= 0) {
|
|
||||||
this.notifyError("제목을 입력하세요")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.is_loading) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.is_loading = true
|
|
||||||
let res = await api.saveCategory(this.title)
|
|
||||||
if (res.data.success === true) {
|
|
||||||
this.title = ''
|
|
||||||
this.category_list = []
|
|
||||||
this.selected_category = null
|
|
||||||
this.cancelCreateCategory()
|
|
||||||
await this.getCategoryList()
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async deleteCategory() {
|
|
||||||
try {
|
|
||||||
this.is_loading = true
|
|
||||||
let res = await api.deleteCategory(this.modify_selected_category.categoryId)
|
|
||||||
if (res.data.success === true) {
|
|
||||||
this.title = ''
|
|
||||||
this.category_list = []
|
|
||||||
this.selected_category = null
|
|
||||||
this.cancelCreateCategory()
|
|
||||||
await this.getCategoryList()
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async modifyCategory() {
|
|
||||||
if (this.title.trim().length <= 0) {
|
|
||||||
this.notifyError("제목을 입력하세요")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.is_loading) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.is_loading = true
|
|
||||||
let res = await api.modifyCategory(this.modify_selected_category.categoryId, this.title)
|
|
||||||
if (res.data.success === true) {
|
|
||||||
this.title = ''
|
|
||||||
this.category_list = []
|
|
||||||
this.selected_category = null
|
|
||||||
this.cancelCreateCategory()
|
|
||||||
await this.getCategoryList()
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async onDropCategoryCallback() {
|
|
||||||
const ids = this.category_list.map((category) => {
|
|
||||||
return category.categoryId
|
|
||||||
})
|
|
||||||
|
|
||||||
const res = await api.updateCategoryOrders(ids)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.notifySuccess('수정되었습니다.')
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async clickCategory(category) {
|
|
||||||
this.selected_category = category
|
|
||||||
this.page = 1
|
|
||||||
this.total_page = 0
|
|
||||||
this.content_list = []
|
|
||||||
|
|
||||||
await this.getContentInCategory()
|
|
||||||
},
|
|
||||||
|
|
||||||
async getContentInCategory() {
|
|
||||||
try {
|
|
||||||
this.is_loading = true
|
|
||||||
const res = await contentCategoryApi.getContentInCategory(this.selected_category.categoryId, this.page)
|
|
||||||
if (res.data.success === true) {
|
|
||||||
const data = res.data.data
|
|
||||||
|
|
||||||
const total_page = Math.ceil(data.totalCount / 10)
|
|
||||||
this.content_list = data.items
|
|
||||||
|
|
||||||
if (total_page <= 0)
|
|
||||||
this.total_page = 1
|
|
||||||
else
|
|
||||||
this.total_page = total_page
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async next() {
|
|
||||||
await this.getContentInCategory()
|
|
||||||
},
|
|
||||||
|
|
||||||
async searchContentNotInCategory() {
|
|
||||||
if (this.search_word.length < 2) {
|
|
||||||
this.notifyError('검색어를 2글자 이상 입력하세요.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.is_loading = true
|
|
||||||
try {
|
|
||||||
this.is_loading = true
|
|
||||||
const res = await contentCategoryApi.searchContentNotInCategory(
|
|
||||||
this.selected_category.categoryId,
|
|
||||||
this.search_word
|
|
||||||
)
|
|
||||||
|
|
||||||
if (res.data.success === true) {
|
|
||||||
this.search_content_list = res.data.data
|
|
||||||
if (res.data.data.length <= 0) {
|
|
||||||
this.notifyError('검색결과가 없습니다.')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.category-selected-item {
|
|
||||||
background-color: #3bb9f1;
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-data-table__wrapper > table > tbody > .category-content-table-tr > td {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<v-col>
|
<v-col>
|
||||||
<v-btn
|
<v-btn
|
||||||
block
|
block
|
||||||
color="#3bb9f1"
|
color="#9970ff"
|
||||||
dark
|
dark
|
||||||
depressed
|
depressed
|
||||||
@click="showWriteDialog"
|
@click="showWriteDialog"
|
||||||
@@ -50,9 +50,6 @@
|
|||||||
<th class="text-center">
|
<th class="text-center">
|
||||||
가격
|
가격
|
||||||
</th>
|
</th>
|
||||||
<th class="text-center">
|
|
||||||
한정판
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
<th class="text-center">
|
||||||
19금
|
19금
|
||||||
</th>
|
</th>
|
||||||
@@ -65,9 +62,6 @@
|
|||||||
<th class="text-center">
|
<th class="text-center">
|
||||||
등록일
|
등록일
|
||||||
</th>
|
</th>
|
||||||
<th class="text-center">
|
|
||||||
오픈 예정일
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
<th class="text-center">
|
||||||
관리
|
관리
|
||||||
</th>
|
</th>
|
||||||
@@ -100,21 +94,6 @@
|
|||||||
<td v-else>
|
<td v-else>
|
||||||
무료
|
무료
|
||||||
</td>
|
</td>
|
||||||
<td
|
|
||||||
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 }}
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
v-else-if="item.totalContentCount > 0 && item.remainingContentCount <= 0"
|
|
||||||
style="min-width: 100px !important; word-break:break-all; height: auto;"
|
|
||||||
>
|
|
||||||
Sold Out
|
|
||||||
</td>
|
|
||||||
<td v-else>
|
|
||||||
X
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<div v-if="item.isAdult">
|
<div v-if="item.isAdult">
|
||||||
O
|
O
|
||||||
@@ -132,7 +111,6 @@
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ item.date }}</td>
|
<td>{{ item.date }}</td>
|
||||||
<td>{{ item.releaseDate }}</td>
|
|
||||||
<td>
|
<td>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
@@ -227,34 +205,6 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-text>
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
태그
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-text-field
|
|
||||||
v-model="audio_content.tags"
|
|
||||||
label="예 : #연애 #커버곡 #태그"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
가격
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-text-field
|
|
||||||
v-model="audio_content.price"
|
|
||||||
label="가격"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="4">
|
<v-col cols="4">
|
||||||
@@ -287,22 +237,6 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-text>
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="4">
|
|
||||||
포인트 사용 가능
|
|
||||||
</v-col>
|
|
||||||
<v-col
|
|
||||||
cols="8"
|
|
||||||
align="left"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
v-model="audio_content.is_point_available"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions v-show="!is_loading">
|
<v-card-actions v-show="!is_loading">
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
<v-btn
|
<v-btn
|
||||||
@@ -433,46 +367,6 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-text v-show="is_limited_edition === false">
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
예약 공개
|
|
||||||
</v-col>
|
|
||||||
<v-col
|
|
||||||
cols="8"
|
|
||||||
align="left"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
v-model="is_active_reservation"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
<v-row
|
|
||||||
v-if="is_active_reservation"
|
|
||||||
align="center"
|
|
||||||
>
|
|
||||||
<v-col cols="4">
|
|
||||||
예약 날짜와 시간
|
|
||||||
</v-col>
|
|
||||||
<v-col
|
|
||||||
cols="8"
|
|
||||||
align="left"
|
|
||||||
>
|
|
||||||
<DatePicker
|
|
||||||
v-model="reservation_date"
|
|
||||||
value-type="format"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<vue-timepicker
|
|
||||||
v-model="reservation_time"
|
|
||||||
format="HH:mm"
|
|
||||||
input-class="skip-error-style"
|
|
||||||
:minute-interval="15"
|
|
||||||
/>
|
|
||||||
</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">
|
||||||
@@ -484,23 +378,7 @@
|
|||||||
label="가격"
|
label="가격"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
※ 유료 콘텐츠 가격을 입력하면 해당 콘텐츠의 미리듣기 시간을 설정할 수 있습니다.<br>미리듣기 시간은 최소 15초 이상 설정해야 합니다.
|
※ 유료 콘텐츠 가격을 입력하면 해당 콘텐츠의 미리듣기 시간을 설정할 수 있습니다.<br>미리듣기 시간은 최소 30초 이상 설정해야 합니다.
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text v-show="audio_content.price > 0">
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
미리듣기 생성
|
|
||||||
</v-col>
|
|
||||||
<v-col
|
|
||||||
cols="8"
|
|
||||||
align="left"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
v-model="audio_content.is_generate_preview"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
@@ -530,89 +408,6 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-text v-show="audio_content.price > 0">
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
한정판
|
|
||||||
</v-col>
|
|
||||||
<v-col
|
|
||||||
cols="8"
|
|
||||||
align="left"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
v-model="is_limited_edition"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
<v-row
|
|
||||||
v-if="is_limited_edition"
|
|
||||||
align="center"
|
|
||||||
>
|
|
||||||
<v-col cols="4">
|
|
||||||
판매할 콘텐츠 개수
|
|
||||||
</v-col>
|
|
||||||
<v-col
|
|
||||||
cols="8"
|
|
||||||
align="left"
|
|
||||||
>
|
|
||||||
<v-text-field
|
|
||||||
v-model="audio_content.limited"
|
|
||||||
label="판매할 콘텐츠 개수"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text v-show="audio_content.price > 0 && is_limited_edition === false">
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
소장 설정
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-radio-group
|
|
||||||
v-model="audio_content.purchase_option"
|
|
||||||
row
|
|
||||||
>
|
|
||||||
<v-radio
|
|
||||||
value="BOTH"
|
|
||||||
label="소장/대여"
|
|
||||||
/>
|
|
||||||
<v-radio
|
|
||||||
value="BUY_ONLY"
|
|
||||||
label="소장만"
|
|
||||||
/>
|
|
||||||
<v-radio
|
|
||||||
value="RENT_ONLY"
|
|
||||||
label="대여만"
|
|
||||||
/>
|
|
||||||
<v-spacer />
|
|
||||||
</v-radio-group>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text v-show="audio_content.price >= 50">
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
콘텐츠 내용 전체 오픈 설정
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-radio-group
|
|
||||||
v-model="audio_content.is_full_detail_visible"
|
|
||||||
row
|
|
||||||
>
|
|
||||||
<v-radio
|
|
||||||
:value="false"
|
|
||||||
label="구매한 사람만"
|
|
||||||
/>
|
|
||||||
<v-radio
|
|
||||||
:value="true"
|
|
||||||
label="전체"
|
|
||||||
/>
|
|
||||||
<v-spacer />
|
|
||||||
</v-radio-group>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="4">
|
<v-col cols="4">
|
||||||
@@ -645,22 +440,6 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-text>
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="4">
|
|
||||||
포인트 사용 가능
|
|
||||||
</v-col>
|
|
||||||
<v-col
|
|
||||||
cols="8"
|
|
||||||
align="left"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
v-model="audio_content.is_point_available"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions v-show="!is_loading">
|
<v-card-actions v-show="!is_loading">
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
<v-btn
|
<v-btn
|
||||||
@@ -713,41 +492,16 @@
|
|||||||
</v-dialog>
|
</v-dialog>
|
||||||
|
|
||||||
<v-dialog
|
<v-dialog
|
||||||
v-model="show_cropper_dialog"
|
v-model="is_loading"
|
||||||
max-width="800px"
|
max-width="400px"
|
||||||
persistent
|
persistent
|
||||||
>
|
>
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-title>
|
|
||||||
이미지 크롭
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<div class="cropper-wrapper">
|
<v-progress-circular
|
||||||
<img
|
indeterminate
|
||||||
ref="cropper_image"
|
/>
|
||||||
:src="cropper_image_url"
|
|
||||||
alt="Cropper Image"
|
|
||||||
style="max-width: 100%;"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-actions>
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="cancelCropper"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="cropImage"
|
|
||||||
>
|
|
||||||
확인
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</div>
|
</div>
|
||||||
@@ -756,49 +510,32 @@
|
|||||||
<script>
|
<script>
|
||||||
import * as api from '@/api/audio_content'
|
import * as api from '@/api/audio_content'
|
||||||
import VuetifyAudio from 'vuetify-audio'
|
import VuetifyAudio from 'vuetify-audio'
|
||||||
// Main JS (in UMD format)
|
|
||||||
import VueTimepicker from 'vue2-timepicker'
|
|
||||||
import DatePicker from 'vue2-datepicker';
|
|
||||||
import Cropper from 'cropperjs';
|
|
||||||
|
|
||||||
import 'vue2-datepicker/index.css';
|
|
||||||
// CSS
|
|
||||||
import 'vue2-timepicker/dist/VueTimepicker.css'
|
|
||||||
import 'cropperjs/dist/cropper.css';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "AudioContentList",
|
name: "AudioContentList",
|
||||||
|
|
||||||
components: {VuetifyAudio, VueTimepicker, DatePicker},
|
components: {VuetifyAudio},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
is_loading: false,
|
is_loading: false,
|
||||||
is_active_reservation: false,
|
|
||||||
is_limited_edition: false,
|
|
||||||
reservation_date: '',
|
|
||||||
reservation_time: '',
|
|
||||||
show_create_dialog: false,
|
show_create_dialog: false,
|
||||||
show_modify_dialog: false,
|
show_modify_dialog: false,
|
||||||
show_delete_confirm_dialog: false,
|
show_delete_confirm_dialog: false,
|
||||||
show_cropper_dialog: false,
|
|
||||||
cropper_image_url: '',
|
|
||||||
cropper: null,
|
|
||||||
page: 1,
|
page: 1,
|
||||||
total_page: 0,
|
total_page: 0,
|
||||||
search_word: '',
|
search_word: '',
|
||||||
audio_content: {
|
audio_content: {
|
||||||
price: 0,
|
price: 0,
|
||||||
is_adult: false,
|
is_adult: false,
|
||||||
is_point_available: false,
|
|
||||||
is_generate_preview: false,
|
|
||||||
is_comment_available: true,
|
is_comment_available: true,
|
||||||
is_full_detail_visible: true,
|
|
||||||
purchase_option: 'BOTH',
|
|
||||||
},
|
},
|
||||||
audio_contents: [],
|
audio_contents: [],
|
||||||
themeList: [],
|
themeList: [],
|
||||||
selected_audio_content: {},
|
selected_audio_content: {},
|
||||||
|
utm_source: '',
|
||||||
|
utm_medium: '',
|
||||||
|
utm_campaign: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -837,12 +574,11 @@ export default {
|
|||||||
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.price = item.price
|
|
||||||
this.audio_content.is_adult = item.isAdult
|
this.audio_content.is_adult = item.isAdult
|
||||||
this.audio_content.is_point_available = item.isPointAvailable
|
|
||||||
this.audio_content.is_comment_available = item.isCommentAvailable
|
this.audio_content.is_comment_available = item.isCommentAvailable
|
||||||
this.audio_content.cover_image_url = item.coverImageUrl
|
this.audio_content.cover_image_url = item.coverImageUrl
|
||||||
this.audio_content.tags = item.tags
|
console.log(this.audio_content)
|
||||||
|
|
||||||
this.show_modify_dialog = true
|
this.show_modify_dialog = true
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -851,15 +587,8 @@ export default {
|
|||||||
this.audio_content = {
|
this.audio_content = {
|
||||||
price: 0,
|
price: 0,
|
||||||
is_adult: false,
|
is_adult: false,
|
||||||
is_point_available: false,
|
|
||||||
is_comment_available: true,
|
is_comment_available: true,
|
||||||
is_full_detail_visible: true,
|
|
||||||
purchase_option: 'BOTH',
|
|
||||||
}
|
}
|
||||||
this.reservation_time = ''
|
|
||||||
this.reservation_time = ''
|
|
||||||
this.is_active_reservation = false
|
|
||||||
this.is_limited_edition = false
|
|
||||||
this.show_create_dialog = false
|
this.show_create_dialog = false
|
||||||
this.show_modify_dialog = false
|
this.show_modify_dialog = false
|
||||||
this.show_delete_confirm_dialog = false
|
this.show_delete_confirm_dialog = false
|
||||||
@@ -878,63 +607,13 @@ export default {
|
|||||||
imageAdd(payload) {
|
imageAdd(payload) {
|
||||||
const file = payload;
|
const file = payload;
|
||||||
if (file) {
|
if (file) {
|
||||||
// 이미 크롭 처리된 파일인 경우 다시 다이얼로그를 띄우지 않음
|
this.audio_content.cover_image_url = URL.createObjectURL(file)
|
||||||
if (file._isCropped) return;
|
URL.revokeObjectURL(file)
|
||||||
|
|
||||||
this.cropper_image_url = URL.createObjectURL(file)
|
|
||||||
this.show_cropper_dialog = true
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.cropper) {
|
|
||||||
this.cropper.destroy()
|
|
||||||
}
|
|
||||||
this.cropper = new Cropper(this.$refs.cropper_image, {
|
|
||||||
aspectRatio: 1,
|
|
||||||
viewMode: 1,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
this.audio_content.cover_image_url = null
|
this.audio_content.cover_image_url = null
|
||||||
this.audio_content.cover_image = null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelCropper() {
|
|
||||||
this.show_cropper_dialog = false
|
|
||||||
if (this.cropper) {
|
|
||||||
this.cropper.destroy()
|
|
||||||
this.cropper = null
|
|
||||||
}
|
|
||||||
this.cropper_image_url = ''
|
|
||||||
this.audio_content.cover_image = null
|
|
||||||
},
|
|
||||||
|
|
||||||
cropImage() {
|
|
||||||
const canvas = this.cropper.getCroppedCanvas()
|
|
||||||
let finalCanvas = canvas
|
|
||||||
|
|
||||||
const MAX_SIZE = 800
|
|
||||||
if (canvas.width > MAX_SIZE || canvas.height > MAX_SIZE) {
|
|
||||||
const resizeCanvas = document.createElement('canvas')
|
|
||||||
resizeCanvas.width = MAX_SIZE
|
|
||||||
resizeCanvas.height = MAX_SIZE
|
|
||||||
const ctx = resizeCanvas.getContext('2d')
|
|
||||||
ctx.drawImage(canvas, 0, 0, MAX_SIZE, MAX_SIZE)
|
|
||||||
finalCanvas = resizeCanvas
|
|
||||||
}
|
|
||||||
|
|
||||||
finalCanvas.toBlob((blob) => {
|
|
||||||
const file = new File([blob], 'cover_image.png', { type: 'image/png' })
|
|
||||||
file._isCropped = true // 크롭된 파일임을 표시하여 재진입 방지
|
|
||||||
this.audio_content.cover_image = file
|
|
||||||
this.audio_content.cover_image_url = URL.createObjectURL(blob)
|
|
||||||
this.show_cropper_dialog = false
|
|
||||||
if (this.cropper) {
|
|
||||||
this.cropper.destroy()
|
|
||||||
this.cropper = null
|
|
||||||
}
|
|
||||||
}, 'image/png')
|
|
||||||
},
|
|
||||||
|
|
||||||
async getAudioContentThemeList() {
|
async getAudioContentThemeList() {
|
||||||
this.is_loading = true
|
this.is_loading = true
|
||||||
try {
|
try {
|
||||||
@@ -988,16 +667,6 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.is_limited_edition === true) {
|
|
||||||
if (isNaN(parseInt(this.audio_content.limited))) {
|
|
||||||
this.notifyError("한정판 개수는 숫자만 입력가능합니다.")
|
|
||||||
return
|
|
||||||
} else if (this.audio_content.limited < 1) {
|
|
||||||
this.notifyError("한정판 개수를 1 이상 입력해 주세요.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.is_loading) return;
|
if (this.is_loading) return;
|
||||||
|
|
||||||
this.is_loading = true
|
this.is_loading = true
|
||||||
@@ -1011,29 +680,7 @@ export default {
|
|||||||
price: this.audio_content.price,
|
price: this.audio_content.price,
|
||||||
themeId: this.audio_content.theme_id,
|
themeId: this.audio_content.theme_id,
|
||||||
isAdult: this.audio_content.is_adult,
|
isAdult: this.audio_content.is_adult,
|
||||||
isGeneratePreview: this.audio_content.price > 0 ? this.audio_content.is_generate_preview : false,
|
isCommentAvailable: this.audio_content.is_comment_available
|
||||||
purchaseOption: this.audio_content.purchase_option,
|
|
||||||
isPointAvailable: this.audio_content.is_point_available,
|
|
||||||
isCommentAvailable: this.audio_content.is_comment_available,
|
|
||||||
isFullDetailVisible: this.audio_content.is_full_detail_visible
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.audio_content.limited > 0) {
|
|
||||||
request.purchaseOption = 'BUY_ONLY'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.audio_content.price > 0 && this.is_limited_edition === true) {
|
|
||||||
request.limited = this.audio_content.limited
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.is_limited_edition === false && this.is_active_reservation === true) {
|
|
||||||
if (this.reservation_date.trim() === '' || this.reservation_time.trim() === '') {
|
|
||||||
this.notifyError("오픈 예정 날짜와 시간을 선택해 주세요.")
|
|
||||||
this.is_loading = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
request.releaseDate = this.reservation_date + ' ' + this.reservation_time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let previewStartTime = this.audio_content.preview_start_time;
|
let previewStartTime = this.audio_content.preview_start_time;
|
||||||
@@ -1051,8 +698,7 @@ export default {
|
|||||||
!this.isValidTimeFormat(previewStartTime) ||
|
!this.isValidTimeFormat(previewStartTime) ||
|
||||||
!this.isValidTimeFormat(previewEndTime)
|
!this.isValidTimeFormat(previewEndTime)
|
||||||
) {
|
) {
|
||||||
this.notifyError("미리 듣기 시간 형식은 00:00:15 과 같아야 합니다")
|
this.notifyError("미리 듣기 시간 형식은 00:30:00 과 같아야 합니다")
|
||||||
this.is_loading = false
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1063,9 +709,8 @@ export default {
|
|||||||
const totalSeconds2 = h2 * 3600 + m2 * 60 + s2;
|
const totalSeconds2 = h2 * 3600 + m2 * 60 + s2;
|
||||||
const timeDifference = totalSeconds2 - totalSeconds1;
|
const timeDifference = totalSeconds2 - totalSeconds1;
|
||||||
|
|
||||||
if (timeDifference < 15) {
|
if (timeDifference < 30) {
|
||||||
this.notifyError("미리 듣기의 최소 시간은 15초 입니다.")
|
this.notifyError("미리 듣기의 최소 시간은 30초 입니다.")
|
||||||
this.is_loading = false
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1077,7 +722,6 @@ export default {
|
|||||||
(previewEndTime !== undefined && previewEndTime.trim() !== '')
|
(previewEndTime !== undefined && previewEndTime.trim() !== '')
|
||||||
) {
|
) {
|
||||||
this.notifyError("미리 듣기 시작 시간과 종료 시간 둘 다 입력을 하거나 둘 다 입력 하지 않아야 합니다.")
|
this.notifyError("미리 듣기 시작 시간과 종료 시간 둘 다 입력을 하거나 둘 다 입력 하지 않아야 합니다.")
|
||||||
this.is_loading = false
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1155,22 +799,10 @@ export default {
|
|||||||
request.isAdult = this.audio_content.is_adult
|
request.isAdult = this.audio_content.is_adult
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selected_audio_content.isPointAvailable !== this.audio_content.is_point_available) {
|
|
||||||
request.isPointAvailable = this.audio_content.is_point_available
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.selected_audio_content.isCommentAvailable !== this.audio_content.is_comment_available) {
|
if (this.selected_audio_content.isCommentAvailable !== this.audio_content.is_comment_available) {
|
||||||
request.isCommentAvailable = this.audio_content.is_comment_available
|
request.isCommentAvailable = this.audio_content.is_comment_available
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.audio_content.price !== this.selected_audio_content.price) {
|
|
||||||
request.price = this.audio_content.price
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.audio_content.tags !== this.selected_audio_content.tags) {
|
|
||||||
request.tags = this.audio_content.tags
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.audio_content.cover_image !== null) {
|
if (this.audio_content.cover_image !== null) {
|
||||||
formData.append("coverImage", this.audio_content.cover_image)
|
formData.append("coverImage", this.audio_content.cover_image)
|
||||||
}
|
}
|
||||||
@@ -1329,9 +961,4 @@ export default {
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cropper-wrapper {
|
|
||||||
max-height: 500px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,517 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<v-container>
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="4">
|
|
||||||
<v-card>
|
|
||||||
<v-img
|
|
||||||
:src="series_detail.coverImageUrl"
|
|
||||||
class="cover-image"
|
|
||||||
/>
|
|
||||||
<v-card-text>
|
|
||||||
제목 : {{ series_detail.title }}
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
소개 :
|
|
||||||
<vue-show-more-text
|
|
||||||
:text="series_detail.introduction"
|
|
||||||
:lines="2"
|
|
||||||
/>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
연재요일 : {{ series_detail.publishedDaysOfWeek }}
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
장르 : {{ series_detail.genre }}
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
키워드 : {{ series_detail.keywords }}
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
연령제한 : <span v-if="series_detail.isAdult">19세이상</span><span v-else>전체이용가</span>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
완결여부 : {{ series_detail.state }}
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text v-show="series_detail.writer !== undefined && series_detail.writer !== null">
|
|
||||||
작가 : {{ series_detail.writer }}
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text v-show="series_detail.studio !== undefined && series_detail.studio !== null">
|
|
||||||
제작사 : {{ series_detail.studio }}
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="8" />
|
|
||||||
<v-col cols="4">
|
|
||||||
<v-btn
|
|
||||||
block
|
|
||||||
color="#3bb9f1"
|
|
||||||
dark
|
|
||||||
depressed
|
|
||||||
@click="showAddContent"
|
|
||||||
>
|
|
||||||
콘텐츠 추가
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-col v-if="series_content_list.length > 0">
|
|
||||||
<v-simple-table>
|
|
||||||
<template>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="text-center">
|
|
||||||
썸네일
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
제목
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
19금
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
관리
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="content in series_content_list"
|
|
||||||
:key="content.contentId"
|
|
||||||
>
|
|
||||||
<td align="center">
|
|
||||||
<v-img
|
|
||||||
:src="content.coverImage"
|
|
||||||
max-width="100"
|
|
||||||
class="content-cover-image"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
|
||||||
{{ content.title }}
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
|
||||||
<div v-if="content.isAdult">
|
|
||||||
O
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
X
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
|
||||||
<v-btn
|
|
||||||
color="#3bb9f1"
|
|
||||||
@click="deleteConfirm(content)"
|
|
||||||
>
|
|
||||||
삭제
|
|
||||||
</v-btn>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</template>
|
|
||||||
</v-simple-table>
|
|
||||||
<br><br>
|
|
||||||
<v-row
|
|
||||||
v-if="total_page > 0"
|
|
||||||
class="text-center"
|
|
||||||
>
|
|
||||||
<v-col>
|
|
||||||
<v-pagination
|
|
||||||
v-model="page"
|
|
||||||
:length="total_page"
|
|
||||||
circle
|
|
||||||
@input="next"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
<v-col
|
|
||||||
v-else
|
|
||||||
class="no-series-content"
|
|
||||||
>
|
|
||||||
등록된 콘텐츠가 없습니다.
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
|
|
||||||
<v-dialog
|
|
||||||
v-model="show_add_series_content"
|
|
||||||
max-width="1000px"
|
|
||||||
persistent
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-title>
|
|
||||||
콘텐츠 추가
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-text>
|
|
||||||
<v-text-field
|
|
||||||
v-model="search_word"
|
|
||||||
label="콘텐츠 제목"
|
|
||||||
@keyup.enter="searchContentNotInSeries"
|
|
||||||
>
|
|
||||||
<v-btn
|
|
||||||
slot="append"
|
|
||||||
color="#3bb9f1"
|
|
||||||
dark
|
|
||||||
@click="searchContentNotInSeries"
|
|
||||||
>
|
|
||||||
검색
|
|
||||||
</v-btn>
|
|
||||||
</v-text-field>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text v-if="search_content_list.length > 0 || add_content_list.length > 0">
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
검색결과
|
|
||||||
<v-simple-table>
|
|
||||||
<template v-slot:default>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="text-center">
|
|
||||||
제목
|
|
||||||
</th>
|
|
||||||
<th />
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="content in search_content_list"
|
|
||||||
:key="content.contentId"
|
|
||||||
>
|
|
||||||
<td>{{ content.title }}</td>
|
|
||||||
<td>
|
|
||||||
<v-btn
|
|
||||||
color="#3bb9f1"
|
|
||||||
@click="addContent(content)"
|
|
||||||
>
|
|
||||||
추가
|
|
||||||
</v-btn>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</template>
|
|
||||||
</v-simple-table>
|
|
||||||
</v-col>
|
|
||||||
<v-col v-if="add_content_list.length > 0">
|
|
||||||
추가할 콘텐츠
|
|
||||||
<v-simple-table>
|
|
||||||
<template>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="text-center">
|
|
||||||
제목
|
|
||||||
</th>
|
|
||||||
<th />
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="content in add_content_list"
|
|
||||||
:key="content.contentId"
|
|
||||||
>
|
|
||||||
<td>{{ content.title }}</td>
|
|
||||||
<td>
|
|
||||||
<v-btn
|
|
||||||
color="#3bb9f1"
|
|
||||||
@click="removeContent(content)"
|
|
||||||
>
|
|
||||||
제거
|
|
||||||
</v-btn>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</template>
|
|
||||||
</v-simple-table>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions v-show="!is_loading">
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="cancel"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="addContentInSeries"
|
|
||||||
>
|
|
||||||
추가
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
|
|
||||||
<v-dialog
|
|
||||||
v-model="show_delete_confirm_dialog"
|
|
||||||
max-width="400px"
|
|
||||||
persistent
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-text />
|
|
||||||
<v-card-text v-if="selected_series_content !== null">
|
|
||||||
{{ selected_series_content.title }} 삭제하시겠습니까?
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text v-else>
|
|
||||||
삭제하시겠습니까?
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions v-show="!is_loading">
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="deleteCancel"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="removeContentInSeries"
|
|
||||||
>
|
|
||||||
확인
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
|
|
||||||
<v-dialog
|
|
||||||
v-model="is_loading"
|
|
||||||
max-width="400px"
|
|
||||||
persistent
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-text>
|
|
||||||
<v-progress-circular
|
|
||||||
indeterminate
|
|
||||||
/>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as api from '@/api/audio_content_series';
|
|
||||||
|
|
||||||
import VueShowMoreText from 'vue-show-more-text'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "ContentSeriesDetail",
|
|
||||||
|
|
||||||
components: {VueShowMoreText},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
is_loading: false,
|
|
||||||
series_id: 0,
|
|
||||||
series_detail: {},
|
|
||||||
series_content_list: [],
|
|
||||||
selected_series_content: null,
|
|
||||||
page: 1,
|
|
||||||
total_page: 0,
|
|
||||||
|
|
||||||
search_word: '',
|
|
||||||
add_content_list: [],
|
|
||||||
search_content_list: [],
|
|
||||||
show_add_series_content: false,
|
|
||||||
|
|
||||||
show_delete_confirm_dialog: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
this.series_id = this.$route.params.seriesId
|
|
||||||
if (this.series_id !== undefined && this.series_id > 0) {
|
|
||||||
await this.getSeriesDetail()
|
|
||||||
await this.getSeriesContentList()
|
|
||||||
} else {
|
|
||||||
this.$router.go(-1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
notifyError(message) {
|
|
||||||
this.$dialog.notify.error(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
notifySuccess(message) {
|
|
||||||
this.$dialog.notify.success(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this.search_word = ''
|
|
||||||
this.add_content_list = []
|
|
||||||
this.search_content_list = []
|
|
||||||
this.selected_series_content = null
|
|
||||||
this.show_add_series_content = false
|
|
||||||
this.show_delete_confirm_dialog = false
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteConfirm(series_content) {
|
|
||||||
this.selected_series_content = series_content
|
|
||||||
this.show_delete_confirm_dialog = true
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteCancel() {
|
|
||||||
this.cancel();
|
|
||||||
},
|
|
||||||
|
|
||||||
showAddContent() {
|
|
||||||
this.show_add_series_content = true
|
|
||||||
},
|
|
||||||
|
|
||||||
addContent(content) {
|
|
||||||
this.search_content_list = this.search_content_list.filter((item) => {
|
|
||||||
return item.contentId !== content.contentId
|
|
||||||
});
|
|
||||||
this.add_content_list.push(content)
|
|
||||||
},
|
|
||||||
|
|
||||||
removeContent(content) {
|
|
||||||
this.add_content_list = this.add_content_list.filter((item) => {
|
|
||||||
return item.contentId !== content.contentId
|
|
||||||
});
|
|
||||||
this.search_content_list.push(content)
|
|
||||||
},
|
|
||||||
|
|
||||||
async searchContentNotInSeries() {
|
|
||||||
if (this.search_word.length < 2) {
|
|
||||||
this.notifyError('검색어를 2글자 이상 입력하세요.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await api.searchContentNotInSeries(this.series_id, this.search_word)
|
|
||||||
|
|
||||||
if (res.data.success === true) {
|
|
||||||
this.search_content_list = res.data.data
|
|
||||||
if (res.data.data.length <= 0) {
|
|
||||||
this.notifyError('검색결과가 없습니다.')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async addContentInSeries() {
|
|
||||||
this.is_loading = true
|
|
||||||
const contentIdList = this.add_content_list.map((item) => {
|
|
||||||
return item.contentId
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await api.addingContentToTheSeries(this.series_id, contentIdList)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.cancel()
|
|
||||||
this.page = 1
|
|
||||||
this.total_page = 0
|
|
||||||
await this.getSeriesContentList()
|
|
||||||
} else {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async getSeriesDetail() {
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await api.getSeriesDetail(this.series_id)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.series_detail = res.data.data
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async next() {
|
|
||||||
await this.getSeriesContentList()
|
|
||||||
},
|
|
||||||
|
|
||||||
async getSeriesContentList() {
|
|
||||||
try {
|
|
||||||
const res = await api.getSeriesContent(this.series_id, this.page)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
const data = res.data.data
|
|
||||||
this.series_content_list = data.items
|
|
||||||
|
|
||||||
const total_page = Math.ceil(data.totalCount / 10)
|
|
||||||
if (total_page <= 0)
|
|
||||||
this.total_page = 1
|
|
||||||
else
|
|
||||||
this.total_page = total_page
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '콘텐츠를 불러오지 못했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async removeContentInSeries() {
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await api.removeContentInTheSeries(this.series_id, this.selected_series_content.contentId)
|
|
||||||
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.page = 1
|
|
||||||
this.total_page = 0
|
|
||||||
this.series_content_list = []
|
|
||||||
this.cancel()
|
|
||||||
this.notifySuccess('삭제되었습니다.')
|
|
||||||
await this.getSeriesContentList()
|
|
||||||
} else {
|
|
||||||
this.notifyError('콘텐츠를 삭제하지 못했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('콘텐츠를 삭제하지 못했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.v-card__text {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover-image {
|
|
||||||
aspect-ratio: 1/1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-cover-image {
|
|
||||||
aspect-ratio: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-series-content {
|
|
||||||
height: 50vh;
|
|
||||||
margin-top: 100px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,849 +0,0 @@
|
|||||||
<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="#3bb9f1"
|
|
||||||
dark
|
|
||||||
depressed
|
|
||||||
@click="showWriteDialog"
|
|
||||||
>
|
|
||||||
시리즈 등록
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
<div
|
|
||||||
ref="scroll_container"
|
|
||||||
class="scroll_container"
|
|
||||||
@scroll="onScroll"
|
|
||||||
>
|
|
||||||
<draggable
|
|
||||||
v-model="series_list"
|
|
||||||
class="row"
|
|
||||||
@end="onDropCallback(series_list)"
|
|
||||||
>
|
|
||||||
<v-col
|
|
||||||
v-for="(item, i) in series_list"
|
|
||||||
:key="i"
|
|
||||||
cols="2"
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-title>
|
|
||||||
<v-spacer />
|
|
||||||
<v-img
|
|
||||||
:src="item.coverImageUrl"
|
|
||||||
class="cover-image"
|
|
||||||
@click="selectSeries(item)"
|
|
||||||
/>
|
|
||||||
<v-spacer />
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-text class="series-title-container">
|
|
||||||
{{ item.title }}
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions>
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
@click="showModifyDialog(item)"
|
|
||||||
>
|
|
||||||
수정
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
text
|
|
||||||
@click="deleteConfirm(item)"
|
|
||||||
>
|
|
||||||
삭제
|
|
||||||
</v-btn>
|
|
||||||
<v-spacer />
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
</draggable>
|
|
||||||
</div>
|
|
||||||
</v-container>
|
|
||||||
|
|
||||||
<v-dialog
|
|
||||||
v-model="show_write_dialog"
|
|
||||||
max-width="1000px"
|
|
||||||
persistent
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-title>
|
|
||||||
시리즈 등록
|
|
||||||
</v-card-title>
|
|
||||||
|
|
||||||
<div class="image-select">
|
|
||||||
<label for="image">
|
|
||||||
커버 이미지 등록
|
|
||||||
</label>
|
|
||||||
<v-file-input
|
|
||||||
id="image"
|
|
||||||
v-model="series.cover_image"
|
|
||||||
accept="image/*"
|
|
||||||
@change="imageAdd"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<img
|
|
||||||
v-if="series.cover_image_url"
|
|
||||||
:src="series.cover_image_url"
|
|
||||||
alt=""
|
|
||||||
class="image-preview"
|
|
||||||
>
|
|
||||||
<v-card-text>
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
제목*
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-text-field
|
|
||||||
v-model="series.title"
|
|
||||||
label="제목"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
소개*
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-textarea
|
|
||||||
v-model="series.introduction"
|
|
||||||
label="소개"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="4">
|
|
||||||
연재요일*
|
|
||||||
</v-col>
|
|
||||||
<v-col
|
|
||||||
cols="8"
|
|
||||||
align="left"
|
|
||||||
>
|
|
||||||
<v-row>
|
|
||||||
<v-col
|
|
||||||
v-for="(day, index) in days_of_week"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
:id="'checkbox_' + day.value"
|
|
||||||
v-model="series.published_days_of_week"
|
|
||||||
type="checkbox"
|
|
||||||
:value="day.value"
|
|
||||||
@change="handleCheckboxChange(day.value, $event)"
|
|
||||||
>
|
|
||||||
<label :for="'checkbox_' + day.value"> {{ day.str }}</label><br>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<input
|
|
||||||
id="checkbox_random"
|
|
||||||
v-model="series.published_days_of_week"
|
|
||||||
type="checkbox"
|
|
||||||
value="RANDOM"
|
|
||||||
@change="handleCheckboxChange('RANDOM', $event)"
|
|
||||||
>
|
|
||||||
<label for="checkbox_random"> 랜덤</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">
|
|
||||||
<v-select
|
|
||||||
v-model="series.genre_id"
|
|
||||||
:items="series_genre_list"
|
|
||||||
item-text="name"
|
|
||||||
item-value="value"
|
|
||||||
label="장르 선택"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text v-show="selected_series === undefined || selected_series === null">
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
키워드*
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-text-field
|
|
||||||
v-model="series.keyword"
|
|
||||||
label="키워드 예 : #달달 #장기연애 #첫사랑"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="4">
|
|
||||||
연령제한
|
|
||||||
</v-col>
|
|
||||||
<v-col
|
|
||||||
cols="8"
|
|
||||||
align="left"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
v-model="series.is_adult"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text v-show="selected_series !== undefined && selected_series !== null">
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="4">
|
|
||||||
완결여부*
|
|
||||||
</v-col>
|
|
||||||
<v-col
|
|
||||||
cols="8"
|
|
||||||
align="left"
|
|
||||||
>
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
<input
|
|
||||||
id="radio_proceeding"
|
|
||||||
v-model="series.state"
|
|
||||||
type="radio"
|
|
||||||
value="PROCEEDING"
|
|
||||||
>
|
|
||||||
<label for="radio_proceeding"> 연재중</label>
|
|
||||||
</v-col>
|
|
||||||
<v-col>
|
|
||||||
<input
|
|
||||||
id="radio_suspend"
|
|
||||||
v-model="series.state"
|
|
||||||
type="radio"
|
|
||||||
value="SUSPEND"
|
|
||||||
>
|
|
||||||
<label for="radio_suspend"> 휴재중</label>
|
|
||||||
</v-col>
|
|
||||||
<v-col>
|
|
||||||
<input
|
|
||||||
id="radio_complete"
|
|
||||||
v-model="series.state"
|
|
||||||
type="radio"
|
|
||||||
value="COMPLETE"
|
|
||||||
>
|
|
||||||
<label for="radio_complete"> 완결</label>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
작가
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-text-field
|
|
||||||
v-model="series.writer"
|
|
||||||
label="작가"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-text>
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
제작사
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-text-field
|
|
||||||
v-model="series.studio"
|
|
||||||
label="제작사"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions v-show="!is_loading">
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="cancel"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
v-if="selected_series !== undefined && selected_series !== 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-dialog
|
|
||||||
v-model="show_delete_confirm_dialog"
|
|
||||||
max-width="400px"
|
|
||||||
persistent
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-text />
|
|
||||||
<v-card-text>
|
|
||||||
삭제하시겠습니까?
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions v-show="!is_loading">
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="deleteCancel"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="deleteSeries"
|
|
||||||
>
|
|
||||||
확인
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
|
|
||||||
<v-dialog
|
|
||||||
v-model="show_cropper_dialog"
|
|
||||||
max-width="800px"
|
|
||||||
persistent
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-title>이미지 크롭 (A4 비율)</v-card-title>
|
|
||||||
<v-card-text>
|
|
||||||
<div class="cropper-wrapper">
|
|
||||||
<img
|
|
||||||
ref="cropper_image"
|
|
||||||
:src="cropper_image_url"
|
|
||||||
alt="Cropper Image"
|
|
||||||
class="cropper-image"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions>
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="grey darken-1"
|
|
||||||
text
|
|
||||||
@click="cancelCropper"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="cropImage"
|
|
||||||
>
|
|
||||||
크롭 완료
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
|
|
||||||
<v-dialog
|
|
||||||
v-model="is_loading"
|
|
||||||
max-width="400px"
|
|
||||||
persistent
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-text>
|
|
||||||
<v-progress-circular
|
|
||||||
indeterminate
|
|
||||||
/>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as api from '@/api/audio_content_series';
|
|
||||||
import Draggable from "vuedraggable";
|
|
||||||
import Cropper from 'cropperjs';
|
|
||||||
import 'cropperjs/dist/cropper.css';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "ContentSeriesList",
|
|
||||||
components: {Draggable},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
is_loading: false,
|
|
||||||
show_write_dialog: false,
|
|
||||||
show_delete_confirm_dialog: false,
|
|
||||||
series: {is_adult: false, published_days_of_week: []},
|
|
||||||
series_list: [],
|
|
||||||
selected_series: null,
|
|
||||||
series_genre_list: [],
|
|
||||||
show_cropper_dialog: false,
|
|
||||||
cropper_image_url: '',
|
|
||||||
cropper: null,
|
|
||||||
page: 1,
|
|
||||||
total_page: 0,
|
|
||||||
days_of_week: [
|
|
||||||
{value: 'SUN', str: '일'},
|
|
||||||
{value: 'MON', str: '월'},
|
|
||||||
{value: 'TUE', str: '화'},
|
|
||||||
{value: 'WED', str: '수'},
|
|
||||||
{value: 'THU', str: '목'},
|
|
||||||
{value: 'FRI', str: '금'},
|
|
||||||
{value: 'SAT', str: '토'},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
await this.getSeriesGenreList();
|
|
||||||
await this.getSeriesList();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async onScroll() {
|
|
||||||
const scrollContainer = this.$refs.scroll_container;
|
|
||||||
const threshold = scrollContainer.scrollHeight - scrollContainer.clientHeight;
|
|
||||||
if (scrollContainer.scrollTop >= threshold - 10 && this.total_page >= this.page) {
|
|
||||||
await this.getSeriesList();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
imageAdd(payload) {
|
|
||||||
const file = payload;
|
|
||||||
if (file) {
|
|
||||||
if (file._isCropped) return;
|
|
||||||
|
|
||||||
this.cropper_image_url = URL.createObjectURL(file)
|
|
||||||
this.show_cropper_dialog = true
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.cropper) {
|
|
||||||
this.cropper.destroy()
|
|
||||||
}
|
|
||||||
this.cropper = new Cropper(this.$refs.cropper_image, {
|
|
||||||
aspectRatio: 210 / 297,
|
|
||||||
viewMode: 1,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.series.cover_image_url = null
|
|
||||||
this.series.cover_image = null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelCropper() {
|
|
||||||
this.show_cropper_dialog = false
|
|
||||||
if (this.cropper) {
|
|
||||||
this.cropper.destroy()
|
|
||||||
this.cropper = null
|
|
||||||
}
|
|
||||||
this.cropper_image_url = ''
|
|
||||||
this.series.cover_image = null
|
|
||||||
},
|
|
||||||
|
|
||||||
cropImage() {
|
|
||||||
const canvas = this.cropper.getCroppedCanvas()
|
|
||||||
let finalCanvas = canvas
|
|
||||||
|
|
||||||
const MAX_WIDTH = 1000
|
|
||||||
if (canvas.width > MAX_WIDTH) {
|
|
||||||
const height = MAX_WIDTH * (297 / 210)
|
|
||||||
const resizeCanvas = document.createElement('canvas')
|
|
||||||
resizeCanvas.width = MAX_WIDTH
|
|
||||||
resizeCanvas.height = height
|
|
||||||
const ctx = resizeCanvas.getContext('2d')
|
|
||||||
ctx.drawImage(canvas, 0, 0, MAX_WIDTH, height)
|
|
||||||
finalCanvas = resizeCanvas
|
|
||||||
}
|
|
||||||
|
|
||||||
finalCanvas.toBlob((blob) => {
|
|
||||||
const file = new File([blob], 'cover_image.png', {type: 'image/png'})
|
|
||||||
file._isCropped = true
|
|
||||||
this.series.cover_image = file
|
|
||||||
this.series.cover_image_url = URL.createObjectURL(blob)
|
|
||||||
this.show_cropper_dialog = false
|
|
||||||
if (this.cropper) {
|
|
||||||
this.cropper.destroy()
|
|
||||||
this.cropper = null
|
|
||||||
}
|
|
||||||
}, 'image/png')
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError(message) {
|
|
||||||
this.$dialog.notify.error(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
notifySuccess(message) {
|
|
||||||
this.$dialog.notify.success(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
showWriteDialog() {
|
|
||||||
this.show_write_dialog = true
|
|
||||||
},
|
|
||||||
|
|
||||||
showModifyDialog(series) {
|
|
||||||
this.series = {
|
|
||||||
title: series.title,
|
|
||||||
introduction: series.introduction,
|
|
||||||
published_days_of_week: series.publishedDaysOfWeek,
|
|
||||||
genre_id: series.genreId,
|
|
||||||
is_adult: series.isAdult,
|
|
||||||
state: series.state,
|
|
||||||
cover_image_url: series.coverImageUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
if (series.writer !== undefined && series.writer !== null) {
|
|
||||||
this.series.writer = series.writer
|
|
||||||
}
|
|
||||||
|
|
||||||
if (series.studio !== undefined && series.studio !== null) {
|
|
||||||
this.series.studio = series.studio
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selected_series = series
|
|
||||||
this.show_write_dialog = true
|
|
||||||
},
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this.series = {is_adult: false, published_days_of_week: []}
|
|
||||||
this.selected_series = null
|
|
||||||
this.show_write_dialog = false
|
|
||||||
this.show_delete_confirm_dialog = false
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteConfirm(series) {
|
|
||||||
this.selected_series = series
|
|
||||||
this.show_delete_confirm_dialog = true
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteCancel() {
|
|
||||||
this.cancel();
|
|
||||||
},
|
|
||||||
|
|
||||||
handleCheckboxChange(checked_days_of_week, event) {
|
|
||||||
if (checked_days_of_week === 'RANDOM' && event.target.checked) {
|
|
||||||
this.series.published_days_of_week = ['RANDOM'];
|
|
||||||
} else {
|
|
||||||
this.series.published_days_of_week = this.series.published_days_of_week.filter(item => item !== 'RANDOM');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
validate() {
|
|
||||||
if (this.series.cover_image === undefined || this.series.cover_image === null) {
|
|
||||||
this.notifyError('커버 이미지를 선택하세요')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.series.title === undefined || this.series.title === null || this.series.title.trim().length <= 0) {
|
|
||||||
this.notifyError('시리즈 제목을 입력하세요')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.series.introduction === undefined || this.series.introduction === null || this.series.introduction.trim().length <= 0) {
|
|
||||||
this.notifyError('시리즈 소개를 입력하세요')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.series.published_days_of_week === undefined || this.series.published_days_of_week === null || this.series.published_days_of_week.length <= 0) {
|
|
||||||
this.notifyError('시리즈 연재요일을 선택하세요')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.series.keyword === undefined || this.series.keyword === null || this.series.keyword.replaceAll('#', '').trim().length <= 0) {
|
|
||||||
this.notifyError('시리즈를 설명할 수 있는 키워드를 입력하세요')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.series.genre_id === undefined || this.series.genre_id === null || this.series.genre_id <= 0) {
|
|
||||||
this.notifyError('올바른 장르를 선택하세요')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.submit();
|
|
||||||
},
|
|
||||||
|
|
||||||
selectSeries(series) {
|
|
||||||
this.$router.push({name: 'ContentSeriesDetail', params: {seriesId: series.seriesId}})
|
|
||||||
},
|
|
||||||
|
|
||||||
async onDropCallback(items) {
|
|
||||||
const ids = items.map((item) => {
|
|
||||||
return item.seriesId
|
|
||||||
})
|
|
||||||
|
|
||||||
const res = await api.updateSeriesOrders(ids)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.notifySuccess(res.data.message)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async getSeriesGenreList() {
|
|
||||||
try {
|
|
||||||
const res = await api.getGenreList()
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.series_genre_list = res.data.data.map((item) => {
|
|
||||||
return {name: item.genre, value: item.id}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async getSeriesList() {
|
|
||||||
if (this.is_loading) return;
|
|
||||||
|
|
||||||
this.is_loading = true
|
|
||||||
try {
|
|
||||||
const res = await api.getSeriesList(this.page)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
const data = res.data.data
|
|
||||||
const total_page = Math.ceil(data.totalCount / 10)
|
|
||||||
|
|
||||||
if (this.page === 1) this.series_list.length = 0;
|
|
||||||
this.page += 1;
|
|
||||||
this.series_list.push(...data.items);
|
|
||||||
|
|
||||||
if (total_page <= 0)
|
|
||||||
this.total_page = 1
|
|
||||||
else
|
|
||||||
this.total_page = total_page
|
|
||||||
} else {
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.is_loading = false
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async submit() {
|
|
||||||
if (this.is_loading) return;
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const request = {
|
|
||||||
title: this.series.title,
|
|
||||||
introduction: this.series.introduction,
|
|
||||||
publishedDaysOfWeek: this.series.published_days_of_week,
|
|
||||||
keyword: this.series.keyword,
|
|
||||||
genreId: this.series.genre_id,
|
|
||||||
isAdult: this.series.is_adult,
|
|
||||||
writer: this.series.writer || null,
|
|
||||||
studio: this.series.studio || null
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append("image", this.series.cover_image);
|
|
||||||
formData.append("request", JSON.stringify(request))
|
|
||||||
|
|
||||||
const res = await api.createSeries(formData);
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.cancel();
|
|
||||||
this.notifySuccess(res.data.message || '등록되었습니다.')
|
|
||||||
this.page = 1
|
|
||||||
await this.getSeriesList()
|
|
||||||
} else {
|
|
||||||
this.is_loading = false
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async modify() {
|
|
||||||
if (this.is_loading) return;
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const request = {seriesId: this.selected_series.seriesId}
|
|
||||||
if (this.selected_series.title !== this.series.title) {
|
|
||||||
request.title = this.series.title
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.selected_series.introduction !== this.series.introduction) {
|
|
||||||
request.introduction = this.series.introduction
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.selected_series.publishedDaysOfWeek !== this.series.published_days_of_week) {
|
|
||||||
request.publishedDaysOfWeek = this.series.published_days_of_week
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.selected_series.genreId !== this.series.genre_id) {
|
|
||||||
request.genreId = this.series.genre_id
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.selected_series.isAdult !== this.series.is_adult) {
|
|
||||||
request.isAdult = this.series.is_adult
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.selected_series.state !== this.series.state) {
|
|
||||||
request.state = this.series.state
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.selected_series.state !== this.series.state) {
|
|
||||||
request.state = this.series.state
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.selected_series.writer !== this.series.writer) {
|
|
||||||
request.writer = this.series.writer
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.selected_series.studio !== this.series.studio) {
|
|
||||||
request.studio = this.series.studio
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append("request", JSON.stringify(request))
|
|
||||||
|
|
||||||
if (this.series.cover_image !== undefined && this.series.cover_image !== null) {
|
|
||||||
formData.append("image", this.series.cover_image);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await api.modifySeries(formData);
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.cancel();
|
|
||||||
this.notifySuccess(res.data.message || '등록되었습니다.')
|
|
||||||
this.page = 1
|
|
||||||
await this.getSeriesList()
|
|
||||||
} else {
|
|
||||||
this.is_loading = false
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async deleteSeries() {
|
|
||||||
if (this.is_loading) return;
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const request = {seriesId: this.selected_series.seriesId, isActive: false}
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append("request", JSON.stringify(request))
|
|
||||||
|
|
||||||
const res = await api.modifySeries(formData)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.cancel();
|
|
||||||
this.notifySuccess('시리즈가 삭제되었습니다.')
|
|
||||||
this.page = 1
|
|
||||||
await this.getSeriesList()
|
|
||||||
} else {
|
|
||||||
this.is_loading = false
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.scroll_container {
|
|
||||||
margin-top: 30px;
|
|
||||||
height: 100vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll_container > .row {
|
|
||||||
padding-bottom: 280px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.series-title-container {
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
max-height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover-image {
|
|
||||||
aspect-ratio: 210/297;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cropper-wrapper {
|
|
||||||
max-height: 70vh;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cropper-image {
|
|
||||||
display: block;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,16 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app>
|
<v-app>
|
||||||
<v-main>
|
<v-main>
|
||||||
<v-container fluid>
|
<v-container
|
||||||
<v-row
|
align-center
|
||||||
align="start"
|
justify-center
|
||||||
justify="center"
|
|
||||||
style="padding-top: 10vh;"
|
|
||||||
>
|
|
||||||
<v-col
|
|
||||||
cols="12"
|
|
||||||
sm="8"
|
|
||||||
md="4"
|
|
||||||
>
|
>
|
||||||
<v-card class="elevation-12">
|
<v-card class="elevation-12">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
@@ -39,41 +32,13 @@
|
|||||||
로그인
|
로그인
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
<v-divider />
|
|
||||||
<v-card-text class="text-center">
|
|
||||||
<div
|
|
||||||
style="display: flex; flex-direction: column; align-items: center; gap: 10px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
id="google-login-btn"
|
|
||||||
style="width: 192px; height: 45px; display: flex; align-items: center; justify-content: center;"
|
|
||||||
/>
|
|
||||||
<v-btn
|
|
||||||
width="192"
|
|
||||||
height="45"
|
|
||||||
class="pa-0"
|
|
||||||
elevation="0"
|
|
||||||
color="transparent"
|
|
||||||
@click="loginKakao"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="@/assets/kakao_login.png"
|
|
||||||
alt="카카오 로그인 버튼"
|
|
||||||
style="width: 100%; height: 100%; display: block;"
|
|
||||||
>
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
</v-container>
|
||||||
</v-main>
|
</v-main>
|
||||||
</v-app>
|
</v-app>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* global Kakao */
|
|
||||||
export default {
|
export default {
|
||||||
name: "Login",
|
name: "Login",
|
||||||
|
|
||||||
@@ -83,86 +48,7 @@ export default {
|
|||||||
password: '',
|
password: '',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.initGoogleLogin();
|
|
||||||
this.initKakaoLogin();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
initKakaoLogin() {
|
|
||||||
if (typeof Kakao !== 'undefined') {
|
|
||||||
if (!Kakao.isInitialized()) {
|
|
||||||
Kakao.init(process.env.VUE_APP_KAKAO_JS_KEY);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.initKakaoLogin();
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
loginKakao() {
|
|
||||||
if (typeof Kakao !== 'undefined') {
|
|
||||||
if (!Kakao.isInitialized()) {
|
|
||||||
this.initKakaoLogin();
|
|
||||||
this.notifyError('카카오 SDK가 초기화되지 않았습니다. 잠시 후 다시 시도해주세요.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Kakao.Auth && typeof Kakao.Auth.login === 'function') {
|
|
||||||
Kakao.Auth.login({
|
|
||||||
success: (authObj) => {
|
|
||||||
this.$store.dispatch('accountStore/LOGIN_KAKAO', { accessToken: authObj.access_token })
|
|
||||||
.then(() => {
|
|
||||||
this.$router.push(this.$route.query.redirect || '/')
|
|
||||||
})
|
|
||||||
.catch((message) => {
|
|
||||||
this.notifyError(message);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
this.notifyError('카카오 로그인에 실패했습니다.');
|
|
||||||
console.error(err);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.notifyError('카카오 인증 모듈을 불러오지 못했습니다. 페이지를 새로고침 해주세요.');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.initKakaoLogin();
|
|
||||||
this.notifyError('카카오 SDK를 불러오는 중입니다. 잠시 후 다시 시도해주세요.');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initGoogleLogin() {
|
|
||||||
/* global google */
|
|
||||||
if (typeof google !== 'undefined') {
|
|
||||||
google.accounts.id.initialize({
|
|
||||||
client_id: process.env.VUE_APP_GOOGLE_CLIENT_ID,
|
|
||||||
callback: this.handleCredentialResponse
|
|
||||||
});
|
|
||||||
google.accounts.id.renderButton(
|
|
||||||
document.getElementById("google-login-btn"),
|
|
||||||
{ theme: "outline", size: "large", width: 192 }
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.initGoogleLogin();
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleCredentialResponse(response) {
|
|
||||||
const idToken = response.credential;
|
|
||||||
this.$store.dispatch('accountStore/LOGIN_GOOGLE', { idToken })
|
|
||||||
.then(() => {
|
|
||||||
this.$router.push(this.$route.query.redirect || '/')
|
|
||||||
})
|
|
||||||
.catch((message) => {
|
|
||||||
this.notifyError(message);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyError: async function (message) {
|
notifyError: async function (message) {
|
||||||
await this.$dialog.notify.error(message)
|
await this.$dialog.notify.error(message)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,825 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<v-toolbar dark>
|
|
||||||
<v-spacer />
|
|
||||||
<v-toolbar-title>시그니처 관리</v-toolbar-title>
|
|
||||||
<v-spacer />
|
|
||||||
</v-toolbar>
|
|
||||||
<v-container>
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="10" />
|
|
||||||
<v-col>
|
|
||||||
<v-btn
|
|
||||||
block
|
|
||||||
color="#3bb9f1"
|
|
||||||
dark
|
|
||||||
depressed
|
|
||||||
@click="showWriteDialog"
|
|
||||||
>
|
|
||||||
시그니처 등록
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
<v-row class="row-sort">
|
|
||||||
<v-col>
|
|
||||||
<input
|
|
||||||
id="sort-newest"
|
|
||||||
v-model="sort_type"
|
|
||||||
type="radio"
|
|
||||||
value="NEWEST"
|
|
||||||
class="radio-sort"
|
|
||||||
@change="changeSortType"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
for="sort-newest"
|
|
||||||
class="radio-label-sort"
|
|
||||||
>
|
|
||||||
최신순
|
|
||||||
</label>
|
|
||||||
</v-col>
|
|
||||||
<v-col>
|
|
||||||
<input
|
|
||||||
id="sort-can-high"
|
|
||||||
v-model="sort_type"
|
|
||||||
type="radio"
|
|
||||||
value="CAN_HIGH"
|
|
||||||
class="radio-sort"
|
|
||||||
@change="changeSortType"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
for="sort-can-high"
|
|
||||||
class="radio-label-sort"
|
|
||||||
>
|
|
||||||
높은캔순
|
|
||||||
</label>
|
|
||||||
</v-col>
|
|
||||||
<v-col>
|
|
||||||
<input
|
|
||||||
id="sort-can-low"
|
|
||||||
v-model="sort_type"
|
|
||||||
type="radio"
|
|
||||||
value="CAN_LOW"
|
|
||||||
class="radio-sort"
|
|
||||||
@change="changeSortType"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
for="sort-can-low"
|
|
||||||
class="radio-label-sort"
|
|
||||||
>
|
|
||||||
낮은캔순
|
|
||||||
</label>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="10" />
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
<v-data-table
|
|
||||||
:headers="headers"
|
|
||||||
:items="signature_list"
|
|
||||||
:loading="is_loading"
|
|
||||||
:items-per-page="-1"
|
|
||||||
item-key="id"
|
|
||||||
class="elevation-1"
|
|
||||||
hide-default-footer
|
|
||||||
>
|
|
||||||
<template v-slot:item.can="{ item }">
|
|
||||||
{{ item.can }}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.isAdult="{ item }">
|
|
||||||
<h3 v-if="item.isAdult">
|
|
||||||
O
|
|
||||||
</h3>
|
|
||||||
<h3 v-else>
|
|
||||||
X
|
|
||||||
</h3>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.image="{ item }">
|
|
||||||
<v-img
|
|
||||||
:src="item.image"
|
|
||||||
max-width="200"
|
|
||||||
max-height="200"
|
|
||||||
align="center"
|
|
||||||
class="center-image"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-slot:item.management="{ item }">
|
|
||||||
<v-row>
|
|
||||||
<v-col />
|
|
||||||
<v-col>
|
|
||||||
<v-btn
|
|
||||||
:disabled="is_loading"
|
|
||||||
@click="showModifyDialog(item)"
|
|
||||||
>
|
|
||||||
수정
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
<v-col>
|
|
||||||
<v-btn
|
|
||||||
:disabled="is_loading"
|
|
||||||
@click="showDeleteConfirm(item)"
|
|
||||||
>
|
|
||||||
삭제
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
<v-col />
|
|
||||||
</v-row>
|
|
||||||
</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-text-field
|
|
||||||
v-model="can"
|
|
||||||
label="캔"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-text>
|
|
||||||
<v-row>
|
|
||||||
<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"
|
|
||||||
accept="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-text>
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
시간(초)
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-text-field
|
|
||||||
v-model="time"
|
|
||||||
label="시간(초)"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-actions v-show="!is_loading">
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="cancel"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="validate"
|
|
||||||
>
|
|
||||||
등록
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-dialog
|
|
||||||
v-model="show_modify_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-text-field
|
|
||||||
v-model="can"
|
|
||||||
label="캔"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-text>
|
|
||||||
<v-row>
|
|
||||||
<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"
|
|
||||||
accept="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-text>
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="4">
|
|
||||||
시간(초)
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-text-field
|
|
||||||
v-model="time"
|
|
||||||
label="시간(초)"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-actions v-show="!is_loading">
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="cancel"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="modifySignatureCan"
|
|
||||||
>
|
|
||||||
수정
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</v-row>
|
|
||||||
<v-row>
|
|
||||||
<v-dialog
|
|
||||||
v-model="show_delete_confirm_dialog"
|
|
||||||
max-width="400px"
|
|
||||||
persistent
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-text />
|
|
||||||
<v-card-text>
|
|
||||||
삭제하시겠습니까?
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions v-show="!is_loading">
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="cancel"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="deleteSignature"
|
|
||||||
>
|
|
||||||
확인
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<v-dialog
|
|
||||||
v-model="show_cropper_dialog"
|
|
||||||
max-width="800px"
|
|
||||||
persistent
|
|
||||||
>
|
|
||||||
<v-card>
|
|
||||||
<v-card-title>이미지 크롭</v-card-title>
|
|
||||||
<v-card-text>
|
|
||||||
<div class="cropper-wrapper">
|
|
||||||
<img
|
|
||||||
ref="cropper_image"
|
|
||||||
:src="cropper_image_url"
|
|
||||||
alt="Cropper Image"
|
|
||||||
class="cropper-image"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions>
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
|
||||||
color="grey darken-1"
|
|
||||||
text
|
|
||||||
@click="cancelCropper"
|
|
||||||
>
|
|
||||||
취소
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
color="blue darken-1"
|
|
||||||
text
|
|
||||||
@click="cropImage"
|
|
||||||
>
|
|
||||||
크롭 완료
|
|
||||||
</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as api from "@/api/signature";
|
|
||||||
import Cropper from 'cropperjs';
|
|
||||||
import 'cropperjs/dist/cropper.css';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "SignatureManagement",
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
is_loading: false,
|
|
||||||
show_write_dialog: false,
|
|
||||||
show_modify_dialog: false,
|
|
||||||
show_delete_confirm_dialog: false,
|
|
||||||
|
|
||||||
signature_list: [],
|
|
||||||
|
|
||||||
page: 1,
|
|
||||||
total_page: 0,
|
|
||||||
|
|
||||||
can: 0,
|
|
||||||
time: 7,
|
|
||||||
image: null,
|
|
||||||
is_adult: false,
|
|
||||||
image_url: null,
|
|
||||||
is_active: null,
|
|
||||||
selected_signature_can: {},
|
|
||||||
sort_type: 'NEWEST',
|
|
||||||
|
|
||||||
show_cropper_dialog: false,
|
|
||||||
cropper_image_url: '',
|
|
||||||
cropper: null,
|
|
||||||
|
|
||||||
headers: [
|
|
||||||
{
|
|
||||||
text: '캔',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'can',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '19금',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'isAdult',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '이미지',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'image',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '시간(초)',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'time',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '관리',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
value: 'management',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
await this.getSignatureList();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
notifyError(message) {
|
|
||||||
this.$dialog.notify.error(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
notifySuccess(message) {
|
|
||||||
this.$dialog.notify.success(message)
|
|
||||||
},
|
|
||||||
|
|
||||||
imageAdd(payload) {
|
|
||||||
const file = payload;
|
|
||||||
if (file) {
|
|
||||||
if (file._isCropped) return;
|
|
||||||
|
|
||||||
if (file.type === 'image/gif') {
|
|
||||||
this.image = file
|
|
||||||
this.image_url = URL.createObjectURL(file)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cropper_image_url = URL.createObjectURL(file)
|
|
||||||
this.show_cropper_dialog = true
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.cropper) {
|
|
||||||
this.cropper.destroy()
|
|
||||||
}
|
|
||||||
this.cropper = new Cropper(this.$refs.cropper_image, {
|
|
||||||
viewMode: 1,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.image_url = null
|
|
||||||
this.image = null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelCropper() {
|
|
||||||
this.show_cropper_dialog = false
|
|
||||||
if (this.cropper) {
|
|
||||||
this.cropper.destroy()
|
|
||||||
this.cropper = null
|
|
||||||
}
|
|
||||||
this.cropper_image_url = ''
|
|
||||||
this.image = null
|
|
||||||
this.image_url = null
|
|
||||||
},
|
|
||||||
|
|
||||||
cropImage() {
|
|
||||||
const canvas = this.cropper.getCroppedCanvas()
|
|
||||||
let finalCanvas = canvas
|
|
||||||
|
|
||||||
const MAX_WIDTH = 800
|
|
||||||
if (canvas.width > MAX_WIDTH) {
|
|
||||||
const height = (MAX_WIDTH * canvas.height) / canvas.width
|
|
||||||
const resizeCanvas = document.createElement('canvas')
|
|
||||||
resizeCanvas.width = MAX_WIDTH
|
|
||||||
resizeCanvas.height = height
|
|
||||||
const ctx = resizeCanvas.getContext('2d')
|
|
||||||
ctx.drawImage(canvas, 0, 0, MAX_WIDTH, height)
|
|
||||||
finalCanvas = resizeCanvas
|
|
||||||
}
|
|
||||||
|
|
||||||
finalCanvas.toBlob((blob) => {
|
|
||||||
const file = new File([blob], 'signature_image.png', {type: 'image/png'})
|
|
||||||
file._isCropped = true
|
|
||||||
this.image = file
|
|
||||||
this.image_url = URL.createObjectURL(blob)
|
|
||||||
this.show_cropper_dialog = false
|
|
||||||
if (this.cropper) {
|
|
||||||
this.cropper.destroy()
|
|
||||||
this.cropper = null
|
|
||||||
}
|
|
||||||
}, 'image/png')
|
|
||||||
},
|
|
||||||
|
|
||||||
showWriteDialog() {
|
|
||||||
this.show_write_dialog = true
|
|
||||||
},
|
|
||||||
|
|
||||||
validate() {
|
|
||||||
if (
|
|
||||||
this.can === 0 ||
|
|
||||||
this.image === null
|
|
||||||
) {
|
|
||||||
this.notifyError('내용을 입력하세요')
|
|
||||||
} else if (this.time < 3 || this.time > 20) {
|
|
||||||
this.notifyError('시간은 3초 이상 20초 이하를 입력하세요.')
|
|
||||||
} else {
|
|
||||||
this.submit()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this.show_write_dialog = false
|
|
||||||
this.show_modify_dialog = false
|
|
||||||
this.show_delete_confirm_dialog = false
|
|
||||||
this.image = null
|
|
||||||
this.image_url = null
|
|
||||||
this.can = 0
|
|
||||||
this.time = 7
|
|
||||||
this.is_adult = false
|
|
||||||
this.is_active = null
|
|
||||||
this.selected_signature_can = {}
|
|
||||||
},
|
|
||||||
|
|
||||||
showDeleteConfirm(item) {
|
|
||||||
this.selected_signature_can = item
|
|
||||||
this.show_delete_confirm_dialog = true
|
|
||||||
},
|
|
||||||
|
|
||||||
showModifyDialog(item) {
|
|
||||||
this.can = item.can;
|
|
||||||
this.time = item.time;
|
|
||||||
this.is_adult = item.isAdult
|
|
||||||
this.image_url = item.image
|
|
||||||
this.selected_signature_can = item
|
|
||||||
this.show_modify_dialog = true
|
|
||||||
},
|
|
||||||
|
|
||||||
async changeSortType() {
|
|
||||||
await this.getSignatureList();
|
|
||||||
},
|
|
||||||
|
|
||||||
async getSignatureList() {
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
let res = await api.getSignatureList(this.page, this.sort_type);
|
|
||||||
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
const data = res.data.data
|
|
||||||
|
|
||||||
const total_page = Math.ceil(data.totalCount / 20)
|
|
||||||
this.signature_list = data.items
|
|
||||||
|
|
||||||
if (total_page <= 0)
|
|
||||||
this.total_page = 1
|
|
||||||
else
|
|
||||||
this.total_page = total_page
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async submit() {
|
|
||||||
if (this.is_loading) return;
|
|
||||||
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append("can", this.can)
|
|
||||||
formData.append("time", this.time)
|
|
||||||
formData.append("image", this.image)
|
|
||||||
formData.append("isAdult", this.is_adult)
|
|
||||||
|
|
||||||
const res = await api.createSignature(formData)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.cancel()
|
|
||||||
this.notifySuccess(res.data.message || '등록되었습니다.')
|
|
||||||
|
|
||||||
this.page = 1
|
|
||||||
await this.getSignatureList()
|
|
||||||
} else {
|
|
||||||
this.is_loading = false
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
this.is_loading = false
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async modifySignatureCan() {
|
|
||||||
if (
|
|
||||||
this.image === null &&
|
|
||||||
this.is_adult === this.selected_signature_can.isAdult &&
|
|
||||||
this.can === this.selected_signature_can.can &&
|
|
||||||
this.time === this.selected_signature_can.time
|
|
||||||
) {
|
|
||||||
this.notifyError('변경사항이 없습니다.')
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.is_loading) return;
|
|
||||||
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append("id", this.selected_signature_can.id)
|
|
||||||
|
|
||||||
if (this.image !== null) {
|
|
||||||
formData.append("image", this.image)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.is_adult !== this.selected_signature_can.isAdult) {
|
|
||||||
formData.append("isAdult", this.is_adult)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.can !== this.selected_signature_can.can) {
|
|
||||||
formData.append("can", this.can)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.time !== this.selected_signature_can.time) {
|
|
||||||
formData.append("time", this.time)
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await api.modifySignature(formData)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.cancel()
|
|
||||||
this.notifySuccess(res.data.message || '수정되었습니다.')
|
|
||||||
|
|
||||||
this.page = 1
|
|
||||||
await this.getSignatureList()
|
|
||||||
} else {
|
|
||||||
this.is_loading = false
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
this.is_loading = false
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async deleteSignature() {
|
|
||||||
if (this.is_loading) return;
|
|
||||||
|
|
||||||
this.is_loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append("id", this.selected_signature_can.id)
|
|
||||||
formData.append("isActive", false)
|
|
||||||
|
|
||||||
const res = await api.modifySignature(formData)
|
|
||||||
if (res.status === 200 && res.data.success === true) {
|
|
||||||
this.cancel()
|
|
||||||
this.notifySuccess(res.data.message || '등록되었습니다.')
|
|
||||||
|
|
||||||
this.page = 1
|
|
||||||
await this.getSignatureList()
|
|
||||||
} else {
|
|
||||||
this.is_loading = false
|
|
||||||
this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
|
|
||||||
this.is_loading = false
|
|
||||||
} finally {
|
|
||||||
this.is_loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async next() {
|
|
||||||
await this.getSignatureList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-card__text {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-card__actions {
|
|
||||||
margin-top: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-card__actions > .v-btn {
|
|
||||||
font-size: 20px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-image {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row-sort {
|
|
||||||
margin-top: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-sort {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-label-sort {
|
|
||||||
font-weight: normal;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-sort:checked + .radio-label-sort {
|
|
||||||
font-weight: bold;
|
|
||||||
color: #3bb9f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cropper-wrapper {
|
|
||||||
max-height: 500px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cropper-image {
|
|
||||||
display: block;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
print_usage() {
|
|
||||||
echo "Usage:"
|
|
||||||
echo " $0"
|
|
||||||
echo " $0 <commit-hash>"
|
|
||||||
echo " $0 --message \"<commit-message>\""
|
|
||||||
echo " $0 --message-file <file-path>"
|
|
||||||
}
|
|
||||||
|
|
||||||
load_commit_message() {
|
|
||||||
if [ $# -eq 0 ]; then
|
|
||||||
local commit_ref="HEAD"
|
|
||||||
echo "Checking latest commit..." >&2
|
|
||||||
git log -1 --pretty=format:"%s%n%b" "$commit_ref"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
-h|--help)
|
|
||||||
print_usage
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
--message)
|
|
||||||
shift
|
|
||||||
if [ $# -eq 0 ]; then
|
|
||||||
echo "[FAIL] --message option requires a commit message"
|
|
||||||
print_usage
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Checking provided commit message..." >&2
|
|
||||||
printf '%s' "$*"
|
|
||||||
;;
|
|
||||||
--message-file)
|
|
||||||
shift
|
|
||||||
if [ $# -ne 1 ]; then
|
|
||||||
echo "[FAIL] --message-file option requires a file path"
|
|
||||||
print_usage
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ ! -f "$1" ]; then
|
|
||||||
echo "[FAIL] Commit message file not found: $1"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Checking commit message file: $1" >&2
|
|
||||||
cat "$1"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
if [ $# -ne 1 ]; then
|
|
||||||
echo "[FAIL] Invalid arguments"
|
|
||||||
print_usage
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local commit_ref="$1"
|
|
||||||
if ! git rev-parse --verify "$commit_ref^{commit}" >/dev/null 2>&1; then
|
|
||||||
echo "[FAIL] Invalid commit reference: $commit_ref"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Checking commit: $commit_ref" >&2
|
|
||||||
git log -1 --pretty=format:"%s%n%b" "$commit_ref"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
commit_message=$(load_commit_message "$@")
|
|
||||||
subject=$(printf '%s\n' "$commit_message" | head -n1)
|
|
||||||
body=$(printf '%s\n' "$commit_message" | tail -n +2)
|
|
||||||
|
|
||||||
echo "Checking commit message format..."
|
|
||||||
echo "Subject: $subject"
|
|
||||||
|
|
||||||
exit_code=0
|
|
||||||
|
|
||||||
if [ -z "$subject" ]; then
|
|
||||||
echo "[FAIL] Subject must not be empty"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
subject_pattern='^([a-z]+)(\([a-z0-9._/-]+\))?(!)?: (.+)$'
|
|
||||||
if [[ "$subject" =~ $subject_pattern ]]; then
|
|
||||||
type="${BASH_REMATCH[1]}"
|
|
||||||
description="${BASH_REMATCH[4]}"
|
|
||||||
|
|
||||||
echo "[PASS] Subject follows Conventional Commit format"
|
|
||||||
echo "[PASS] Type is lowercase: $type"
|
|
||||||
|
|
||||||
if printf '%s\n' "$description" | grep -Eq '[가-힣]'; then
|
|
||||||
echo "[PASS] Description contains Korean text"
|
|
||||||
else
|
|
||||||
echo "[FAIL] Description must contain Korean text"
|
|
||||||
exit_code=1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "[FAIL] Subject must match: <type>(scope): <description>"
|
|
||||||
echo " scope is optional, example: feat: 기능을 추가한다"
|
|
||||||
exit_code=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$body" ] && printf '%s\n' "$body" | grep -Eq '^Refs:'; then
|
|
||||||
while IFS= read -r refs_line; do
|
|
||||||
if ! printf '%s\n' "$refs_line" | grep -Eq '^Refs: #[0-9]+(, #[0-9]+)*$'; then
|
|
||||||
echo "[FAIL] Refs footer format is invalid: $refs_line"
|
|
||||||
echo " expected format: Refs: #123 or Refs: #123, #456"
|
|
||||||
exit_code=1
|
|
||||||
fi
|
|
||||||
done < <(printf '%s\n' "$body" | grep -E '^Refs:')
|
|
||||||
|
|
||||||
if [ $exit_code -eq 0 ]; then
|
|
||||||
echo "[PASS] Refs footer format is valid"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $exit_code -eq 0 ]; then
|
|
||||||
echo "[PASS] Commit message follows AGENTS.md rules"
|
|
||||||
else
|
|
||||||
echo "[FAIL] Commit message violates AGENTS.md rules"
|
|
||||||
fi
|
|
||||||
|
|
||||||
exit $exit_code
|
|
||||||
Reference in New Issue
Block a user