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