first commit
This commit is contained in:
		
							
								
								
									
										2
									
								
								.env.development
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.env.development
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| VUE_APP_API_URL=https://test-api.sodalive.net | ||||
| NODE_ENV=development | ||||
							
								
								
									
										2
									
								
								.env.production
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.env.production
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| VUE_APP_API_URL=https://api.sodalive.net | ||||
| NODE_ENV=production | ||||
							
								
								
									
										221
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,221 @@ | ||||
| .DS_Store | ||||
| node_modules | ||||
| /dist | ||||
|  | ||||
|  | ||||
| # local env files | ||||
| .env.local | ||||
| .env.*.local | ||||
|  | ||||
| # Log files | ||||
| npm-debug.log* | ||||
| yarn-debug.log* | ||||
| yarn-error.log* | ||||
| pnpm-debug.log* | ||||
|  | ||||
| # Editor directories and files | ||||
| .idea | ||||
| .vscode | ||||
| *.suo | ||||
| *.ntvs* | ||||
| *.njsproj | ||||
| *.sln | ||||
| *.sw? | ||||
|  | ||||
|  | ||||
| # Created by https://www.toptal.com/developers/gitignore/api/webstorm,visualstudiocode,vuejs,macos,windows | ||||
| # Edit at https://www.toptal.com/developers/gitignore?templates=webstorm,visualstudiocode,vuejs,macos,windows | ||||
|  | ||||
| ### macOS ### | ||||
| # General | ||||
| .AppleDouble | ||||
| .LSOverride | ||||
|  | ||||
| # Icon must end with two \r | ||||
| Icon | ||||
|  | ||||
|  | ||||
| # Thumbnails | ||||
| ._* | ||||
|  | ||||
| # Files that might appear in the root of a volume | ||||
| .DocumentRevisions-V100 | ||||
| .fseventsd | ||||
| .Spotlight-V100 | ||||
| .TemporaryItems | ||||
| .Trashes | ||||
| .VolumeIcon.icns | ||||
| .com.apple.timemachine.donotpresent | ||||
|  | ||||
| # Directories potentially created on remote AFP share | ||||
| .AppleDB | ||||
| .AppleDesktop | ||||
| Network Trash Folder | ||||
| Temporary Items | ||||
| .apdisk | ||||
|  | ||||
| ### VisualStudioCode ### | ||||
| .vscode/* | ||||
| !.vscode/settings.json | ||||
| !.vscode/tasks.json | ||||
| !.vscode/launch.json | ||||
| !.vscode/extensions.json | ||||
| !.vscode/*.code-snippets | ||||
|  | ||||
| # Local History for Visual Studio Code | ||||
| .history/ | ||||
|  | ||||
| # Built Visual Studio Code Extensions | ||||
| *.vsix | ||||
|  | ||||
| ### VisualStudioCode Patch ### | ||||
| # Ignore all local history of files | ||||
| .history | ||||
| .ionide | ||||
|  | ||||
| # Support for Project snippet scope | ||||
|  | ||||
| ### Vuejs ### | ||||
| # Recommended template: Node.gitignore | ||||
|  | ||||
| node_modules/ | ||||
| dist/ | ||||
| npm-debug.log | ||||
| yarn-error.log | ||||
|  | ||||
| ### WebStorm ### | ||||
| # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider | ||||
| # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 | ||||
|  | ||||
| # User-specific stuff | ||||
| .idea/**/workspace.xml | ||||
| .idea/**/tasks.xml | ||||
| .idea/**/usage.statistics.xml | ||||
| .idea/**/dictionaries | ||||
| .idea/**/shelf | ||||
|  | ||||
| # AWS User-specific | ||||
| .idea/**/aws.xml | ||||
|  | ||||
| # Generated files | ||||
| .idea/**/contentModel.xml | ||||
|  | ||||
| # Sensitive or high-churn files | ||||
| .idea/**/dataSources/ | ||||
| .idea/**/dataSources.ids | ||||
| .idea/**/dataSources.local.xml | ||||
| .idea/**/sqlDataSources.xml | ||||
| .idea/**/dynamic.xml | ||||
| .idea/**/uiDesigner.xml | ||||
| .idea/**/dbnavigator.xml | ||||
|  | ||||
| # Gradle | ||||
| .idea/**/gradle.xml | ||||
| .idea/**/libraries | ||||
|  | ||||
| # Gradle and Maven with auto-import | ||||
| # When using Gradle or Maven with auto-import, you should exclude module files, | ||||
| # since they will be recreated, and may cause churn.  Uncomment if using | ||||
| # auto-import. | ||||
| # .idea/artifacts | ||||
| # .idea/compiler.xml | ||||
| # .idea/jarRepositories.xml | ||||
| # .idea/modules.xml | ||||
| # .idea/*.iml | ||||
| # .idea/modules | ||||
| # *.iml | ||||
| # *.ipr | ||||
|  | ||||
| # CMake | ||||
| cmake-build-*/ | ||||
|  | ||||
| # Mongo Explorer plugin | ||||
| .idea/**/mongoSettings.xml | ||||
|  | ||||
| # File-based project format | ||||
| *.iws | ||||
|  | ||||
| # IntelliJ | ||||
| out/ | ||||
|  | ||||
| # mpeltonen/sbt-idea plugin | ||||
| .idea_modules/ | ||||
|  | ||||
| # JIRA plugin | ||||
| atlassian-ide-plugin.xml | ||||
|  | ||||
| # Cursive Clojure plugin | ||||
| .idea/replstate.xml | ||||
|  | ||||
| # SonarLint plugin | ||||
| .idea/sonarlint/ | ||||
|  | ||||
| # Crashlytics plugin (for Android Studio and IntelliJ) | ||||
| com_crashlytics_export_strings.xml | ||||
| crashlytics.properties | ||||
| crashlytics-build.properties | ||||
| fabric.properties | ||||
|  | ||||
| # Editor-based Rest Client | ||||
| .idea/httpRequests | ||||
|  | ||||
| # Android studio 3.1+ serialized cache file | ||||
| .idea/caches/build_file_checksums.ser | ||||
|  | ||||
| ### WebStorm Patch ### | ||||
| # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 | ||||
|  | ||||
| # *.iml | ||||
| # modules.xml | ||||
| # .idea/misc.xml | ||||
| # *.ipr | ||||
|  | ||||
| # Sonarlint plugin | ||||
| # https://plugins.jetbrains.com/plugin/7973-sonarlint | ||||
| .idea/**/sonarlint/ | ||||
|  | ||||
| # SonarQube Plugin | ||||
| # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin | ||||
| .idea/**/sonarIssues.xml | ||||
|  | ||||
| # Markdown Navigator plugin | ||||
| # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced | ||||
| .idea/**/markdown-navigator.xml | ||||
| .idea/**/markdown-navigator-enh.xml | ||||
| .idea/**/markdown-navigator/ | ||||
|  | ||||
| # Cache file creation bug | ||||
| # See https://youtrack.jetbrains.com/issue/JBR-2257 | ||||
| .idea/$CACHE_FILE$ | ||||
|  | ||||
| # CodeStream plugin | ||||
| # https://plugins.jetbrains.com/plugin/12206-codestream | ||||
| .idea/codestream.xml | ||||
|  | ||||
| ### Windows ### | ||||
| # Windows thumbnail cache files | ||||
| Thumbs.db | ||||
| Thumbs.db:encryptable | ||||
| ehthumbs.db | ||||
| ehthumbs_vista.db | ||||
|  | ||||
| # Dump file | ||||
| *.stackdump | ||||
|  | ||||
| # Folder config file | ||||
| [Dd]esktop.ini | ||||
|  | ||||
| # Recycle Bin used on file shares | ||||
| $RECYCLE.BIN/ | ||||
|  | ||||
| # Windows Installer files | ||||
| *.cab | ||||
| *.msi | ||||
| *.msix | ||||
| *.msm | ||||
| *.msp | ||||
|  | ||||
| # Windows shortcuts | ||||
| *.lnk | ||||
|  | ||||
| # End of https://www.toptal.com/developers/gitignore/api/webstorm,visualstudiocode,vuejs,macos,windows | ||||
							
								
								
									
										24
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| # soda-creator-admin | ||||
|  | ||||
| ## Project setup | ||||
| ``` | ||||
| npm install | ||||
| ``` | ||||
|  | ||||
| ### Compiles and hot-reloads for development | ||||
| ``` | ||||
| npm run serve | ||||
| ``` | ||||
|  | ||||
| ### Compiles and minifies for production | ||||
| ``` | ||||
| npm run build | ||||
| ``` | ||||
|  | ||||
| ### Lints and fixes files | ||||
| ``` | ||||
| npm run lint | ||||
| ``` | ||||
|  | ||||
| ### Customize configuration | ||||
| See [Configuration Reference](https://cli.vuejs.org/config/). | ||||
							
								
								
									
										5
									
								
								babel.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								babel.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| module.exports = { | ||||
|   presets: [ | ||||
|     '@vue/cli-plugin-babel/preset' | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										28462
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										28462
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										63
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| { | ||||
|   "name": "soda-creator-admin", | ||||
|   "version": "0.1.0", | ||||
|   "private": true, | ||||
|   "scripts": { | ||||
|     "serve_local": "vue-cli-service serve --mode local --port 8888", | ||||
|     "serve": "vue-cli-service serve --port 8888", | ||||
|     "build": "vue-cli-service build", | ||||
|     "build_development": "vue-cli-service build --mode development", | ||||
|     "lint": "vue-cli-service lint" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "core-js": "^3.6.5", | ||||
|     "vue": "^2.6.11", | ||||
|     "vue-excel-xlsx": "^1.2.2", | ||||
|     "vue-router": "^3.2.0", | ||||
|     "vue2-editor": "^2.10.3", | ||||
|     "vuedraggable": "^2.24.3", | ||||
|     "vuejs-datetimepicker": "^1.1.13", | ||||
|     "vuetify": "2.6.10", | ||||
|     "vuetify-audio": "^0.3.3", | ||||
|     "vuetify-dialog": "^2.0.17", | ||||
|     "vuex": "^3.4.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@vue/cli-plugin-babel": "~4.5.0", | ||||
|     "@vue/cli-plugin-eslint": "~4.5.0", | ||||
|     "@vue/cli-plugin-router": "~4.5.0", | ||||
|     "@vue/cli-plugin-vuex": "~4.5.0", | ||||
|     "@vue/cli-service": "~4.5.0", | ||||
|     "axios": "0.21.2", | ||||
|     "babel-eslint": "^10.1.0", | ||||
|     "eslint": "^6.7.2", | ||||
|     "eslint-plugin-vue": "^6.2.2", | ||||
|     "sass": "~1.32.0", | ||||
|     "sass-loader": "^10.0.0", | ||||
|     "vue-cli-plugin-axios": "~0.0.4", | ||||
|     "vue-cli-plugin-vuetify": "~2.4.5", | ||||
|     "vue-template-compiler": "^2.6.11", | ||||
|     "vuetify-loader": "^1.7.0" | ||||
|   }, | ||||
|   "eslintConfig": { | ||||
|     "root": true, | ||||
|     "env": { | ||||
|       "node": true | ||||
|     }, | ||||
|     "extends": [ | ||||
|       "plugin:vue/recommended", | ||||
|       "eslint:recommended" | ||||
|     ], | ||||
|     "parserOptions": { | ||||
|       "parser": "babel-eslint" | ||||
|     }, | ||||
|     "rules": { | ||||
|       "no-unused-vars": "off" | ||||
|     } | ||||
|   }, | ||||
|   "browserslist": [ | ||||
|     "> 1%", | ||||
|     "last 2 versions", | ||||
|     "not dead" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.2 KiB | 
							
								
								
									
										19
									
								
								public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								public/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="ko"> | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|     <meta name="viewport" content="width=device-width,initial-scale=1.0"> | ||||
|     <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | ||||
|     <title>소다라이브</title> | ||||
|     <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"> | ||||
|   </head> | ||||
|   <body> | ||||
|     <noscript> | ||||
|       <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> | ||||
|     </noscript> | ||||
|     <div id="app"></div> | ||||
|     <!-- built files will be auto injected --> | ||||
|   </body> | ||||
| </html> | ||||
							
								
								
									
										41
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| <template> | ||||
|   <v-app> | ||||
|     <v-app-bar | ||||
|       app | ||||
|       clipped-left | ||||
|       color="primary" | ||||
|       dark | ||||
|     > | ||||
|       <v-spacer /> | ||||
|       <v-toolbar-title>소다라이브 크리에이터</v-toolbar-title> | ||||
|       <v-spacer /> | ||||
|     </v-app-bar> | ||||
|  | ||||
|     <v-main> | ||||
|       <router-view /> | ||||
|     </v-main> | ||||
|   </v-app> | ||||
| </template> | ||||
|  | ||||
| <style lang="scss"> | ||||
| #app { | ||||
|   font-family: Avenir, Helvetica, Arial, sans-serif; | ||||
|   -webkit-font-smoothing: antialiased; | ||||
|   -moz-osx-font-smoothing: grayscale; | ||||
|   text-align: center; | ||||
|   color: #2c3e50; | ||||
| } | ||||
|  | ||||
| #nav { | ||||
|   padding: 30px; | ||||
|  | ||||
|   a { | ||||
|     font-weight: bold; | ||||
|     color: #2c3e50; | ||||
|  | ||||
|     &.router-link-exact-active { | ||||
|       color: #42b983; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										26
									
								
								src/api/audio_content.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/api/audio_content.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| async function getAudioContentList(page) { | ||||
|     return Vue.axios.get( | ||||
|         "/creator-admin/audio-content/list?page=" + (page - 1) + | ||||
|         "&size=10" | ||||
|     ) | ||||
| } | ||||
|  | ||||
| async function searchAudioContent(searchWord, page){ | ||||
|     return Vue.axios.get( | ||||
|         "/creator-admin/audio-content/search?search_word=" + searchWord + | ||||
|         "&page=" + (page - 1) + | ||||
|         "&size=10" | ||||
|     ) | ||||
| } | ||||
|  | ||||
| async function modifyAudioContent(request) { | ||||
|     return Vue.axios.put("/creator-admin/audio-content", request) | ||||
| } | ||||
|  | ||||
| export { | ||||
|     getAudioContentList, | ||||
|     searchAudioContent, | ||||
|     modifyAudioContent | ||||
| } | ||||
							
								
								
									
										11
									
								
								src/api/member.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/api/member.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| async function login(email, password) { | ||||
|     return Vue.axios.post('/creator-admin/member/login', { email, password }); | ||||
| } | ||||
|  | ||||
| async function logout() { | ||||
|     return Vue.axios.post('/creator-admin/member/logout'); | ||||
| } | ||||
|  | ||||
| export { login, logout } | ||||
							
								
								
									
										7
									
								
								src/api/menu.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/api/menu.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| async function getMenus() { | ||||
|     return Vue.axios.get("/menu"); | ||||
| } | ||||
|  | ||||
| export { getMenus } | ||||
							
								
								
									
										
											BIN
										
									
								
								src/assets/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 6.7 KiB | 
							
								
								
									
										122
									
								
								src/components/SideMenu.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/components/SideMenu.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| <template> | ||||
|   <v-navigation-drawer | ||||
|     app | ||||
|     clipped | ||||
|     color="black" | ||||
|     dark | ||||
|     permanent | ||||
|   > | ||||
|     <v-layout | ||||
|       column | ||||
|       fill-height | ||||
|     > | ||||
|       <v-list | ||||
|         v-for="item in items" | ||||
|         :key="item.title" | ||||
|         class="py-0" | ||||
|       > | ||||
|         <div v-if="!item.items"> | ||||
|           <v-list-item | ||||
|             :to="item.route" | ||||
|             active-class="blue white--text" | ||||
|           > | ||||
|             <v-list-item-icon> | ||||
|               <v-icon>{{ item.icon }}</v-icon> | ||||
|             </v-list-item-icon> | ||||
|  | ||||
|             <v-list-item-title>{{ item.title }}</v-list-item-title> | ||||
|           </v-list-item> | ||||
|         </div> | ||||
|  | ||||
|         <v-list-group | ||||
|           v-else | ||||
|           :prepend-icon="item.icon" | ||||
|           no-action | ||||
|         > | ||||
|           <template v-slot:activator> | ||||
|             <v-list-item-title>{{ item.title }}</v-list-item-title> | ||||
|           </template> | ||||
|  | ||||
|           <div | ||||
|             v-for="childItem in item.items" | ||||
|             :key="childItem.title" | ||||
|           > | ||||
|             <v-list-item | ||||
|               :to="childItem.route" | ||||
|               active-class="blue white--text" | ||||
|             > | ||||
|               <v-list-item-title>{{ childItem.title }}</v-list-item-title> | ||||
|             </v-list-item> | ||||
|           </div> | ||||
|         </v-list-group> | ||||
|       </v-list> | ||||
|       <v-spacer /> | ||||
|       <v-list> | ||||
|         <v-list-item @click="logout"> | ||||
|           <v-list-item-icon> | ||||
|             <v-icon>mdi-logout</v-icon> | ||||
|           </v-list-item-icon> | ||||
|  | ||||
|           <v-list-item-title>로그아웃</v-list-item-title> | ||||
|         </v-list-item> | ||||
|       </v-list> | ||||
|     </v-layout> | ||||
|   </v-navigation-drawer> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import * as api from '@/api/menu' | ||||
|  | ||||
| export default { | ||||
|   name: "SideMenu", | ||||
|   data() { | ||||
|     return { | ||||
|       isLoading: false, | ||||
|       items: [], | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   async created() { | ||||
|     await this.getMenus() | ||||
|   }, | ||||
|  | ||||
|   methods: { | ||||
|     notifyError(message) { | ||||
|       this.$dialog.notify.error(message) | ||||
|     }, | ||||
|  | ||||
|     notifySuccess(message) { | ||||
|       this.$dialog.notify.success(message) | ||||
|     }, | ||||
|  | ||||
|     async getMenus() { | ||||
|       this.isLoading = true | ||||
|       try { | ||||
|         let res = await api.getMenus(); | ||||
|         if (res.status === 200 && res.data.success === true && res.data.data.length > 0) { | ||||
|           this.items = res.data.data | ||||
|         } else { | ||||
|           this.notifyError("알 수 없는 오류가 발생했습니다. 다시 로그인 해주세요!") | ||||
|           this.logoutWithoutNetwork(); | ||||
|         } | ||||
|       } catch (e) { | ||||
|         this.notifyError("알 수 없는 오류가 발생했습니다. 다시 로그인 해주세요!") | ||||
|         this.logoutWithoutNetwork(); | ||||
|       } finally { | ||||
|         this.isLoading = false | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     logoutWithoutNetwork() { | ||||
|       this.$store.dispatch('accountStore/LOGOUT_WITHOUT_NETWORK') | ||||
|     }, | ||||
|  | ||||
|     logout() { | ||||
|       this.$store.dispatch('accountStore/LOGOUT') | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| </style> | ||||
							
								
								
									
										21
									
								
								src/layouts/default/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/layouts/default/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <SideMenu /> | ||||
|     <router-view /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import SideMenu from "@/components/SideMenu"; | ||||
|  | ||||
| export default { | ||||
|   name: "DefaultLayout", | ||||
|   components: { | ||||
|     SideMenu | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
|  | ||||
| </style> | ||||
							
								
								
									
										26
									
								
								src/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/main.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import Vue from 'vue' | ||||
| import './plugins/axios' | ||||
| import App from './App.vue' | ||||
| import vuetify from './plugins/vuetify' | ||||
| import router from './router' | ||||
| import store from './store' | ||||
|  | ||||
| import VuetifyDialog from 'vuetify-dialog' | ||||
| import 'vuetify-dialog/dist/vuetify-dialog.css' | ||||
| import VueExcelXlsx from "vue-excel-xlsx"; | ||||
|  | ||||
| Vue.config.productionTip = false | ||||
| Vue.use(VuetifyDialog, { | ||||
|     context: { | ||||
|         vuetify | ||||
|     } | ||||
| }) | ||||
|  | ||||
| Vue.use(VueExcelXlsx); | ||||
|  | ||||
| new Vue({ | ||||
|     vuetify, | ||||
|     router, | ||||
|     store, | ||||
|     render: h => h(App) | ||||
| }).$mount('#app') | ||||
							
								
								
									
										69
									
								
								src/plugins/axios.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/plugins/axios.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| "use strict"; | ||||
|  | ||||
| import Vue from 'vue'; | ||||
| import axios from "axios"; | ||||
|  | ||||
| // Full config:  https://github.com/axios/axios#request-config | ||||
| // axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || ''; | ||||
| // axios.defaults.headers.common['Authorization'] = AUTH_TOKEN; | ||||
| // axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; | ||||
|  | ||||
| let config = { | ||||
|   baseURL: process.env.VUE_APP_API_URL, | ||||
|   timeout: 60 * 1000, // Timeout | ||||
|   // withCredentials: true, // Check cross-site Access-Control | ||||
| }; | ||||
|  | ||||
| const _axios = axios.create(config); | ||||
|  | ||||
| _axios.interceptors.request.use( | ||||
|   function(config) { | ||||
|     // Do something before request is sent | ||||
|     return config; | ||||
|   }, | ||||
|   function(error) { | ||||
|     // Do something with request error | ||||
|     return Promise.reject(error); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| // Add a response interceptor | ||||
| _axios.interceptors.response.use( | ||||
|   function(response) { | ||||
|     // Do something with response data | ||||
|     return response; | ||||
|   }, | ||||
|   function(error) { | ||||
|     // Do something with response error | ||||
|     if (error.response.status === 401) { | ||||
|       localStorage.clear() | ||||
|       if (location.pathname === '/') { | ||||
|         location.reload() | ||||
|       } else { | ||||
|         location.replace("/") | ||||
|       } | ||||
|     } | ||||
|     return Promise.reject(error); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| Plugin.install = function(Vue, options) { | ||||
|   Vue.axios = _axios; | ||||
|   window.axios = _axios; | ||||
|   Object.defineProperties(Vue.prototype, { | ||||
|     axios: { | ||||
|       get() { | ||||
|         return _axios; | ||||
|       } | ||||
|     }, | ||||
|     $axios: { | ||||
|       get() { | ||||
|         return _axios; | ||||
|       } | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| Vue.use(Plugin) | ||||
|  | ||||
| export default Plugin; | ||||
							
								
								
									
										7
									
								
								src/plugins/vuetify.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/plugins/vuetify.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| import Vue from 'vue'; | ||||
| import Vuetify from 'vuetify/lib/framework'; | ||||
|  | ||||
| Vue.use(Vuetify); | ||||
|  | ||||
| export default new Vuetify({ | ||||
| }); | ||||
							
								
								
									
										51
									
								
								src/router/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/router/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| import Vue from 'vue' | ||||
| import VueRouter from 'vue-router' | ||||
| import store from '@/store'; | ||||
|  | ||||
| import DefaultLayout from '@/layouts/default' | ||||
|  | ||||
| Vue.use(VueRouter) | ||||
|  | ||||
| // route level code-splitting | ||||
| // this generates a separate chunk (about.[hash].js) for this route | ||||
| // which is lazy-loaded when the route is visited. | ||||
| const routes = [ | ||||
|     { | ||||
|         path: '/', | ||||
|         name: 'DefaultLayout', | ||||
|         component: DefaultLayout, | ||||
|         children: [ | ||||
|             { | ||||
|                 path: '/content/list', | ||||
|                 name: 'ContentList', | ||||
|                 component: () => import(/* webpackChunkName: "content" */ '../views/Content/ContentList.vue') | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|         path: '/login', | ||||
|         name: 'Login', | ||||
|         component: () => import(/* webpackChunkName: "login" */ '../views/Login/Login.vue') | ||||
|     }, | ||||
| ] | ||||
|  | ||||
| const router = new VueRouter({ | ||||
|     mode: 'history', | ||||
|     base: process.env.BASE_URL, | ||||
|     routes | ||||
| }) | ||||
|  | ||||
| router.beforeEach((to, from, next) => { | ||||
|     if (to.path !== '/login') { | ||||
|         const isAuthenticated = store.getters['accountStore/isAuthenticated'] | ||||
|         if (isAuthenticated) { | ||||
|             next(); | ||||
|         } else { | ||||
|             next('/login?redirect=' + to.fullPath) | ||||
|         } | ||||
|     } else { | ||||
|         next() | ||||
|     } | ||||
| }) | ||||
|  | ||||
| export default router | ||||
							
								
								
									
										123
									
								
								src/store/accountStore.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								src/store/accountStore.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| import * as memberApi from '@/api/member' | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| const enhanceAccessToken = () => { | ||||
|     const {accessToken} = localStorage | ||||
|     if (!accessToken) return | ||||
|     Vue.axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`; | ||||
| } | ||||
|  | ||||
| enhanceAccessToken(); | ||||
|  | ||||
| const accountStore = { | ||||
|     namespaced: true, | ||||
|     state: { | ||||
|         userId: '', | ||||
|         nickname: '', | ||||
|         accessToken: '', | ||||
|         profileImage: '', | ||||
|     }, | ||||
|     getters: { | ||||
|         isAuthenticated(state) { | ||||
|             state.userId = state.userId || localStorage.userId | ||||
|             state.nickname = state.nickname || localStorage.nickname | ||||
|             state.profileImage = state.profileImage || localStorage.profileImage | ||||
|             state.accessToken = state.accessToken || localStorage.accessToken | ||||
|  | ||||
|             return state.accessToken !== undefined && | ||||
|                 state.accessToken !== null && | ||||
|                 state.accessToken !== 'null' && | ||||
|                 state.accessToken !== '' | ||||
|         } | ||||
|     }, | ||||
|     mutations: { | ||||
|         LOGIN(state, {userId, nickname, token, profileImage}) { | ||||
|             state.userId = userId | ||||
|             localStorage.userId = userId | ||||
|  | ||||
|             state.nickname = nickname | ||||
|             localStorage.nickname = nickname | ||||
|  | ||||
|             state.profileImage = profileImage | ||||
|             localStorage.profileImage = profileImage | ||||
|  | ||||
|             state.accessToken = token | ||||
|             localStorage.accessToken = token | ||||
|  | ||||
|             Vue.axios.defaults.headers.common['Authorization'] = `Bearer ${token}`; | ||||
|         }, | ||||
|  | ||||
|         LOGOUT(state) { | ||||
|             state.userId = '' | ||||
|             state.nickname = '' | ||||
|             state.profileImage = '' | ||||
|             state.accessToken = '' | ||||
|  | ||||
|             localStorage.clear() | ||||
|             if (location.pathname === '/') { | ||||
|                 location.reload() | ||||
|             } else { | ||||
|                 location.replace("/") | ||||
|             } | ||||
|         }, | ||||
|     }, | ||||
|     actions: { | ||||
|         async LOGIN({commit}, {email, password}) { | ||||
|             let result = false | ||||
|             let errorMessage = null | ||||
|  | ||||
|             try { | ||||
|                 let res = await memberApi.login(email, password) | ||||
|                 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}) { | ||||
|             let result = false | ||||
|             let errorMessage = null | ||||
|  | ||||
|             try { | ||||
|                 let res = await memberApi.logout() | ||||
|                 if (res.data.success === true) { | ||||
|                     Vue.axios.defaults.headers.common['Authorization'] = undefined; | ||||
|                     commit("LOGOUT") | ||||
|                     result = true | ||||
|                 } else { | ||||
|                     errorMessage = res.data.message | ||||
|                 } | ||||
|             } catch (e) { | ||||
|                 errorMessage = '로그인 정보를 확인해주세요.' | ||||
|             } | ||||
|  | ||||
|             return new Promise((resolve, reject) => { | ||||
|                 if (result) { | ||||
|                     resolve(); | ||||
|                 } else { | ||||
|                     reject(errorMessage) | ||||
|                 } | ||||
|             }); | ||||
|         }, | ||||
|  | ||||
|         LOGOUT_WITHOUT_NETWORK({commit}) { | ||||
|             Vue.axios.defaults.headers.common['Authorization'] = undefined; | ||||
|             commit("LOGOUT") | ||||
|         }, | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default accountStore | ||||
							
								
								
									
										12
									
								
								src/store/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/store/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| import Vue from 'vue' | ||||
| import Vuex from 'vuex' | ||||
|  | ||||
| Vue.use(Vuex) | ||||
|  | ||||
| import accountStore from "@/store/accountStore"; | ||||
|  | ||||
| export default new Vuex.Store({ | ||||
|     modules: { | ||||
|         accountStore: accountStore | ||||
|     } | ||||
| }) | ||||
							
								
								
									
										523
									
								
								src/views/Content/ContentList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										523
									
								
								src/views/Content/ContentList.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,523 @@ | ||||
| <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-simple-table class="elevation-10"> | ||||
|             <template> | ||||
|               <thead> | ||||
|                 <tr> | ||||
|                   <th class="text-center"> | ||||
|                     콘텐츠 번호 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     썸네일 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     제목 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     내용 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     크리에이터 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     테마 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     태그 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     가격 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     19금 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     시간 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     듣기 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     등록일 | ||||
|                   </th> | ||||
|                   <th class="text-center"> | ||||
|                     관리 | ||||
|                   </th> | ||||
|                 </tr> | ||||
|               </thead> | ||||
|               <tbody> | ||||
|                 <tr | ||||
|                   v-for="item in audio_contents" | ||||
|                   :key="item.audioContentId" | ||||
|                 > | ||||
|                   <td>{{ item.audioContentId }}</td> | ||||
|                   <td align="center"> | ||||
|                     <v-img | ||||
|                       max-width="100" | ||||
|                       max-height="100" | ||||
|                       :src="item.coverImageUrl" | ||||
|                       class="rounded-circle" | ||||
|                     /> | ||||
|                   </td> | ||||
|                   <td>{{ item.title }}</td> | ||||
|                   <td style="max-width: 350px !important; word-break:break-all; height: auto;"> | ||||
|                     {{ item.detail }} | ||||
|                   </td> | ||||
|                   <td>{{ item.creatorNickname }}</td> | ||||
|                   <td>{{ item.theme }}</td> | ||||
|                   <td style="max-width: 200px !important; word-break:break-all; height: auto;"> | ||||
|                     {{ item.tags }} | ||||
|                   </td> | ||||
|                   <td v-if="item.price > 0"> | ||||
|                     {{ item.price }} 캔 | ||||
|                   </td> | ||||
|                   <td v-else> | ||||
|                     무료 | ||||
|                   </td> | ||||
|                   <td> | ||||
|                     <div v-if="item.isAdult"> | ||||
|                       O | ||||
|                     </div> | ||||
|                     <div v-else> | ||||
|                       X | ||||
|                     </div> | ||||
|                   </td> | ||||
|                   <td>{{ item.remainingTime }}</td> | ||||
|                   <td> | ||||
|                     <vuetify-audio | ||||
|                       :file="item.contentUrl" | ||||
|                       :downloadable="true" | ||||
|                       :auto-play="false" | ||||
|                     /> | ||||
|                   </td> | ||||
|                   <td>{{ item.date }}</td> | ||||
|                   <td> | ||||
|                     <v-row> | ||||
|                       <v-col> | ||||
|                         <v-btn | ||||
|                           :disabled="is_loading" | ||||
|                           @click="showModifyDialog(item)" | ||||
|                         > | ||||
|                           수정 | ||||
|                         </v-btn> | ||||
|                       </v-col> | ||||
|                       <v-spacer /> | ||||
|                       <v-col> | ||||
|                         <v-btn | ||||
|                           :disabled="is_loading" | ||||
|                           @click="deleteConfirm(item)" | ||||
|                         > | ||||
|                           삭제 | ||||
|                         </v-btn> | ||||
|                       </v-col> | ||||
|                     </v-row> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|               </tbody> | ||||
|             </template> | ||||
|           </v-simple-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_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="audio_content.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-text-field | ||||
|                   v-model="audio_content.detail" | ||||
|                   label="내용" | ||||
|                   required | ||||
|                 /> | ||||
|               </v-col> | ||||
|             </v-row> | ||||
|           </v-card-text> | ||||
|           <v-card-text> | ||||
|             <v-row> | ||||
|               <v-col cols="4"> | ||||
|                 19금 | ||||
|               </v-col> | ||||
|               <v-col | ||||
|                 cols="8" | ||||
|                 align="left" | ||||
|               > | ||||
|                 <input | ||||
|                   v-model="audio_content.is_adult" | ||||
|                   type="checkbox" | ||||
|                 > | ||||
|               </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="audio_content.is_comment_available" | ||||
|                   type="checkbox" | ||||
|                 > | ||||
|               </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="audio_content.is_default_cover_image" | ||||
|                   type="checkbox" | ||||
|                 > | ||||
|               </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="modify" | ||||
|             > | ||||
|               수정 | ||||
|             </v-btn> | ||||
|           </v-card-actions> | ||||
|         </v-card> | ||||
|       </v-dialog> | ||||
|     </v-row> | ||||
|  | ||||
|     <v-dialog | ||||
|       v-model="show_delete_confirm_dialog" | ||||
|       max-width="400px" | ||||
|       persistent | ||||
|     > | ||||
|       <v-card> | ||||
|         <v-card-text /> | ||||
|         <v-card-text> | ||||
|           "{{ selected_audio_content.title }}"을 삭제하시겠습니까? | ||||
|         </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="deleteAudioContent" | ||||
|           > | ||||
|             확인 | ||||
|           </v-btn> | ||||
|         </v-card-actions> | ||||
|       </v-card> | ||||
|     </v-dialog> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import * as api from '@/api/audio_content' | ||||
| import VuetifyAudio from 'vuetify-audio' | ||||
|  | ||||
| export default { | ||||
|     name: "AudioContentList", | ||||
|  | ||||
|     components: {VuetifyAudio}, | ||||
|  | ||||
|     data() { | ||||
|         return { | ||||
|             is_loading: false, | ||||
|             show_modify_dialog: false, | ||||
|             show_delete_confirm_dialog: false, | ||||
|             page: 1, | ||||
|             total_page: 0, | ||||
|             search_word: '', | ||||
|             audio_content: {}, | ||||
|             audio_contents: [], | ||||
|             curations: [], | ||||
|             selected_audio_content: {}, | ||||
|             utm_source: '', | ||||
|             utm_medium: '', | ||||
|             utm_campaign: '', | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     async created() { | ||||
|         await this.getAudioContent() | ||||
|     }, | ||||
|  | ||||
|     methods: { | ||||
|         notifyError(message) { | ||||
|             this.$dialog.notify.error(message) | ||||
|         }, | ||||
|  | ||||
|         notifySuccess(message) { | ||||
|             this.$dialog.notify.success(message) | ||||
|         }, | ||||
|  | ||||
|         deleteConfirm(item) { | ||||
|             this.selected_audio_content = item | ||||
|             this.show_delete_confirm_dialog = true | ||||
|         }, | ||||
|  | ||||
|         deleteCancel() { | ||||
|             this.selected_audio_content = {} | ||||
|             this.show_delete_confirm_dialog = false | ||||
|         }, | ||||
|  | ||||
|         showModifyDialog(item) { | ||||
|             this.selected_audio_content = item | ||||
|  | ||||
|             this.audio_content.id = item.audioContentId | ||||
|             this.audio_content.title = item.title | ||||
|             this.audio_content.detail = item.detail | ||||
|             this.audio_content.curation_id = item.curationId | ||||
|             this.audio_content.is_adult = item.isAdult | ||||
|             this.audio_content.is_comment_available = item.isCommentAvailable | ||||
|             this.audio_content.is_default_cover_image = false | ||||
|  | ||||
|             console.log(this.audio_content) | ||||
|  | ||||
|             this.show_modify_dialog = true | ||||
|         }, | ||||
|  | ||||
|         cancel() { | ||||
|             this.selected_audio_content = {} | ||||
|             this.audio_content = {} | ||||
|             this.show_modify_dialog = false | ||||
|             this.show_delete_confirm_dialog = false | ||||
|         }, | ||||
|  | ||||
|         async modify() { | ||||
|             if ( | ||||
|                 this.audio_content.title === null || | ||||
|                 this.audio_content.title === undefined || | ||||
|                 this.audio_content.title.trim().length <= 0 | ||||
|             ) { | ||||
|                 this.notifyError("제목을 입력하세요") | ||||
|                 return | ||||
|             } | ||||
|  | ||||
|             if ( | ||||
|                 this.audio_content.detail === null || | ||||
|                 this.audio_content.detail === undefined || | ||||
|                 this.audio_content.detail.trim().length <= 0 | ||||
|             ) { | ||||
|                 this.notifyError("내용을 입력하세요") | ||||
|                 return | ||||
|             } | ||||
|  | ||||
|             if (this.is_loading) return; | ||||
|  | ||||
|             this.isLoading = true | ||||
|  | ||||
|             try { | ||||
|                 const request = { | ||||
|                     id: this.audio_content.id, | ||||
|                     isDefaultCoverImage: this.audio_content.is_default_cover_image | ||||
|                 } | ||||
|  | ||||
|                 if ( | ||||
|                     this.selected_audio_content.title !== this.audio_content.title && | ||||
|                     this.audio_content.title.trim().length > 0 | ||||
|                 ) { | ||||
|                     request.title = this.audio_content.title | ||||
|                 } | ||||
|  | ||||
|                 if ( | ||||
|                     this.selected_audio_content.detail !== this.audio_content.detail && | ||||
|                     this.audio_content.detail.trim().length > 0 | ||||
|                 ) { | ||||
|                     request.detail = this.audio_content.detail | ||||
|                 } | ||||
|  | ||||
|                 if (this.selected_audio_content.curationId !== this.audio_content.curation_id) { | ||||
|                     request.curationId = this.audio_content.curation_id | ||||
|                 } | ||||
|  | ||||
|                 if (this.selected_audio_content.isAdult !== this.audio_content.is_adult) { | ||||
|                     request.isAdult = this.audio_content.is_adult | ||||
|                 } | ||||
|  | ||||
|                 if (this.selected_audio_content.isCommentAvailable !== this.audio_content.is_comment_available) { | ||||
|                     request.isCommentAvailable = this.audio_content.is_comment_available | ||||
|                 } | ||||
|  | ||||
|                 console.log(request) | ||||
|                 const res = await api.modifyAudioContent(request) | ||||
|                 if (res.status === 200 && res.data.success === true) { | ||||
|                     this.cancel() | ||||
|                     this.notifySuccess('수정되었습니다.') | ||||
|  | ||||
|                     this.audio_contents = [] | ||||
|                     await this.getAudioContent() | ||||
|                 } else { | ||||
|                     this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||
|                 } | ||||
|             } catch (e) { | ||||
|                 this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||
|             } finally { | ||||
|                 this.is_loading = false | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         async deleteAudioContent() { | ||||
|             if (this.is_loading) return; | ||||
|             this.is_loading = true | ||||
|  | ||||
|             try { | ||||
|                 let request = {id: this.selected_audio_content.audioContentId, isActive: false} | ||||
|  | ||||
|                 const res = await api.modifyAudioContent(request) | ||||
|                 if (res.status === 200 && res.data.success === true) { | ||||
|                     this.cancel() | ||||
|                     this.notifySuccess('삭제되었습니다.') | ||||
|  | ||||
|                     this.audio_contents = [] | ||||
|                     await this.getAudioContent() | ||||
|                 } else { | ||||
|                     this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||
|                 } | ||||
|             } catch (e) { | ||||
|                 this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||
|             } finally { | ||||
|                 this.is_loading = false | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         async next() { | ||||
|             if (this.search_word.length < 2) { | ||||
|                 this.search_word = '' | ||||
|                 await this.getAudioContent() | ||||
|             } else { | ||||
|                 await this.searchAudioContent() | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         async getAudioContent() { | ||||
|             this.is_loading = true | ||||
|             try { | ||||
|                 const res = await api.getAudioContentList(this.page) | ||||
|                 if (res.status === 200 && res.data.success === true) { | ||||
|                     const data = res.data.data | ||||
|  | ||||
|                     const total_page = Math.ceil(data.totalCount / 10) | ||||
|                     this.audio_contents = data.items | ||||
|  | ||||
|                     if (total_page <= 0) | ||||
|                         this.total_page = 1 | ||||
|                     else | ||||
|                         this.total_page = total_page | ||||
|                 } else { | ||||
|                     this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||
|                 } | ||||
|  | ||||
|                 this.is_loading = false | ||||
|             } catch (e) { | ||||
|                 this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||
|                 this.is_loading = false | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         async searchAudioContent() { | ||||
|             if (this.search_word.length === 0) { | ||||
|                 await this.getAudioContent() | ||||
|             } else if (this.search_word.length < 2) { | ||||
|                 this.notifyError('검색어를 2글자 이상 입력하세요.') | ||||
|             } else { | ||||
|                 this.is_loading = true | ||||
|                 try { | ||||
|                     const res = await api.searchAudioContent(this.search_word, this.page) | ||||
|                     if (res.status === 200 && res.data.success === true) { | ||||
|                         const data = res.data.data | ||||
|  | ||||
|                         const total_page = Math.ceil(data.totalCount / 10) | ||||
|                         this.audio_contents = data.items | ||||
|  | ||||
|                         if (total_page <= 0) | ||||
|                             this.total_page = 1 | ||||
|                         else | ||||
|                             this.total_page = total_page | ||||
|                     } else { | ||||
|                         this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||
|                     } | ||||
|  | ||||
|                     this.is_loading = false | ||||
|                 } catch (e) { | ||||
|                     this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.') | ||||
|                     this.is_loading = false | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
|  | ||||
| </style> | ||||
							
								
								
									
										79
									
								
								src/views/Login/Login.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/views/Login/Login.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| <template> | ||||
|   <v-app> | ||||
|     <v-main> | ||||
|       <v-container | ||||
|         align-center | ||||
|         justify-center | ||||
|       > | ||||
|         <v-card class="elevation-12"> | ||||
|           <v-card-text> | ||||
|             <v-form> | ||||
|               <v-text-field | ||||
|                 v-model="email" | ||||
|                 label="Email" | ||||
|                 type="text" | ||||
|               /> | ||||
|               <v-text-field | ||||
|                 v-model="password" | ||||
|                 :append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'" | ||||
|                 :type="showPassword ? 'text' : 'password'" | ||||
|                 label="Password" | ||||
|                 @click:append="showPassword = !showPassword" | ||||
|                 @keyup.enter="loginSubmit" | ||||
|               /> | ||||
|             </v-form> | ||||
|           </v-card-text> | ||||
|           <v-card-actions> | ||||
|             <v-spacer /> | ||||
|             <v-btn | ||||
|               color="primary" | ||||
|               @click="loginSubmit" | ||||
|             > | ||||
|               로그인 | ||||
|             </v-btn> | ||||
|           </v-card-actions> | ||||
|         </v-card> | ||||
|       </v-container> | ||||
|     </v-main> | ||||
|   </v-app> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "Login", | ||||
|  | ||||
|   data: () => ({ | ||||
|     showPassword: false, | ||||
|     email: '', | ||||
|     password: '', | ||||
|   }), | ||||
|  | ||||
|   methods: { | ||||
|     notifyError: async function (message) { | ||||
|       await this.$dialog.notify.error(message) | ||||
|     }, | ||||
|  | ||||
|     loginSubmit() { | ||||
|       let loginData = {}; | ||||
|       loginData.email = this.email; | ||||
|       loginData.password = this.password; | ||||
|  | ||||
|       try { | ||||
|         this.$store.dispatch('accountStore/LOGIN', loginData) | ||||
|           .then(() => { | ||||
|             this.$router.push(this.$route.query.redirect || '/') | ||||
|           }) | ||||
|           .catch((message) => { | ||||
|             this.notifyError(message); | ||||
|           }) | ||||
|       } catch (e) { | ||||
|         this.notifyError('로그인 정보를 확인해주세요.'); | ||||
|       } | ||||
|     }, | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
|  | ||||
| </style> | ||||
							
								
								
									
										13
									
								
								vue.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vue.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| module.exports = { | ||||
|   transpileDependencies: [ | ||||
|     'vuetify' | ||||
|   ], | ||||
|   //adding extract css true solves this issue | ||||
|   css: { | ||||
|     extract: true | ||||
|   }, | ||||
|   configureWebpack: config => { | ||||
|       config.output.filename = 'js/[name].[hash].js' | ||||
|       config.output.chunkFilename = 'js/[name].[hash].js' | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Yu Sung
					Yu Sung