├── .env-sample ├── .gitignore ├── LICENSE ├── README.md ├── assets └── DEMO.gif ├── babel.config.js ├── jsconfig.json ├── package-lock.json ├── package.json ├── public ├── _redirects ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ └── logo.png ├── components │ ├── Aside.vue │ ├── Footer.vue │ ├── Header.vue │ ├── Layout.vue │ ├── SingleModal.vue │ └── UI │ │ ├── Alert │ │ └── Info.vue │ │ ├── Card │ │ ├── AsideCard.vue │ │ └── Card.vue │ │ ├── Loading.vue │ │ ├── TimelineHeader.vue │ │ └── Widget │ │ ├── Albums.vue │ │ └── Filter │ │ ├── Color.vue │ │ ├── Filter.vue │ │ ├── Orientation.vue │ │ ├── SafeSearch.vue │ │ └── Type.vue ├── main.js ├── router │ └── index.js ├── store │ ├── app │ │ ├── actions.js │ │ ├── getters.js │ │ └── mutations.js │ └── index.js └── views │ ├── CategoryView.vue │ ├── HomeView.vue │ ├── LoginView.vue │ └── TagView.vue └── vue.config.js /.env-sample: -------------------------------------------------------------------------------- 1 | VUE_APP_PHOTOS_URL = https://pixabay.com/api/?key=YOUR_API_KEY -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.production 9 | .env.*.local 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | pnpm-debug.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Emre Güney 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Vue Photogram App 3 | A photo gallery app using Vue.js + Vue Router + Vuex 4 | 5 | ![enter image description here](https://github.com/eeguney/vue-photogram-app/blob/master/assets/DEMO.gif) 6 | 7 | ## Features: 8 | 9 | - Using **[Pixabay Api](https://pixabay.com/api/)** 10 | - Home, category, tag and single photo page 11 | - Infinite scroll 12 | - Masonry layout 13 | - Filter by image type, orientation, color and safe search 14 | - Search image 15 | - List albums 16 | - Dark, light and auto mode switch 17 | - Responsive design 18 | 19 | ## Technologies and tools: 20 | 21 | - **Vue.js** 22 | - **VueX, Vue Router** 23 | - **Masonry Layout Wall** 24 | - **Pure SASS** and **SCSS** 25 | - **Responsive** design 26 | 27 | ## Project setup 28 | ``` 29 | npm install 30 | ``` 31 | 32 | ### Compiles and hot-reloads for development 33 | ``` 34 | npm run serve 35 | ``` 36 | 37 | ### Compiles and minifies for production 38 | ``` 39 | npm run build 40 | ``` 41 | 42 | ### Lints and fixes files 43 | ``` 44 | npm run lint 45 | ``` 46 | 47 | ## License: 48 | 49 | MIT License 50 | 51 | Copyright (c) 2022 Emre Güney 52 | 53 | Permission is hereby granted, free of charge, to any person obtaining a copy 54 | of this software and associated documentation files (the "Software"), to deal 55 | in the Software without restriction, including without limitation the rights 56 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 57 | copies of the Software, and to permit persons to whom the Software is 58 | furnished to do so, subject to the following conditions: 59 | 60 | The above copyright notice and this permission notice shall be included in all 61 | copies or substantial portions of the Software. 62 | 63 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 64 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 65 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 66 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 67 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 68 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 69 | SOFTWARE. 70 | -------------------------------------------------------------------------------- /assets/DEMO.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeguney/vue-photogram-app/3755b5687344ae418a4f69239463a68031d9969d/assets/DEMO.gif -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-photogram-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@fortawesome/fontawesome-svg-core": "^6.2.0", 12 | "@fortawesome/free-brands-svg-icons": "^6.2.0", 13 | "@fortawesome/free-regular-svg-icons": "^6.2.0", 14 | "@fortawesome/free-solid-svg-icons": "^6.2.0", 15 | "@fortawesome/vue-fontawesome": "^2.0.8", 16 | "axios": "^1.1.2", 17 | "core-js": "^3.8.3", 18 | "sass": "^1.55.0", 19 | "sass-loader": "^13.1.0", 20 | "vue": "^2.6.14", 21 | "vue-masonry-wall": "^0.3.2", 22 | "vue-router": "^3.5.1", 23 | "vuex": "^3.6.2" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.12.16", 27 | "@babel/eslint-parser": "^7.12.16", 28 | "@vue/cli-plugin-babel": "~5.0.0", 29 | "@vue/cli-plugin-eslint": "~5.0.0", 30 | "@vue/cli-plugin-router": "~5.0.0", 31 | "@vue/cli-plugin-vuex": "~5.0.0", 32 | "@vue/cli-service": "~5.0.0", 33 | "eslint": "^7.32.0", 34 | "eslint-plugin-vue": "^8.0.3", 35 | "vue-template-compiler": "^2.6.14" 36 | }, 37 | "eslintConfig": { 38 | "root": true, 39 | "env": { 40 | "node": true 41 | }, 42 | "extends": [ 43 | "plugin:vue/essential", 44 | "eslint:recommended" 45 | ], 46 | "parserOptions": { 47 | "parser": "@babel/eslint-parser" 48 | }, 49 | "rules": {} 50 | }, 51 | "browserslist": [ 52 | "> 1%", 53 | "last 2 versions", 54 | "not dead" 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeguney/vue-photogram-app/3755b5687344ae418a4f69239463a68031d9969d/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= htmlWebpackPlugin.options.title %> 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 51 | 52 | 455 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eeguney/vue-photogram-app/3755b5687344ae418a4f69239463a68031d9969d/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/Aside.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 39 | 40 | 110 | -------------------------------------------------------------------------------- /src/components/Footer.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | 45 | -------------------------------------------------------------------------------- /src/components/Header.vue: -------------------------------------------------------------------------------- 1 | 99 | 100 | 144 | 145 | 307 | -------------------------------------------------------------------------------- /src/components/Layout.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 85 | 86 | 131 | -------------------------------------------------------------------------------- /src/components/SingleModal.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 70 | 71 | 131 | -------------------------------------------------------------------------------- /src/components/UI/Alert/Info.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/UI/Card/AsideCard.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 59 | 60 | 101 | -------------------------------------------------------------------------------- /src/components/UI/Card/Card.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 59 | 60 | 125 | -------------------------------------------------------------------------------- /src/components/UI/Loading.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | 25 | 75 | -------------------------------------------------------------------------------- /src/components/UI/TimelineHeader.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 43 | 44 | 89 | -------------------------------------------------------------------------------- /src/components/UI/Widget/Albums.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 57 | 58 | 112 | -------------------------------------------------------------------------------- /src/components/UI/Widget/Filter/Color.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/components/UI/Widget/Filter/Filter.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 83 | 84 | 219 | -------------------------------------------------------------------------------- /src/components/UI/Widget/Filter/Orientation.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/components/UI/Widget/Filter/SafeSearch.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/components/UI/Widget/Filter/Type.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import store from "./store"; 5 | import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; 6 | import { 7 | faLock, 8 | faUser, 9 | faTextHeight, 10 | faLink, 11 | faImage, 12 | faBars, 13 | faImages, 14 | faSpinner, 15 | faThumbsUp, 16 | faComment, 17 | faUpDown, 18 | faLeftRight, 19 | faXmark, 20 | faRightFromBracket, 21 | faEllipsisVertical, 22 | faMoon, 23 | faSun, 24 | faFilter, 25 | } from "@fortawesome/free-solid-svg-icons"; 26 | import { faSquare, faKeyboard } from "@fortawesome/free-regular-svg-icons"; 27 | import { faGithub } from "@fortawesome/free-brands-svg-icons"; 28 | import { library } from "@fortawesome/fontawesome-svg-core"; 29 | 30 | library.add( 31 | faUser, 32 | faLock, 33 | faSquare, 34 | faTextHeight, 35 | faKeyboard, 36 | faLink, 37 | faImage, 38 | faBars, 39 | faImages, 40 | faSpinner, 41 | faThumbsUp, 42 | faComment, 43 | faUpDown, 44 | faLeftRight, 45 | faXmark, 46 | faRightFromBracket, 47 | faEllipsisVertical, 48 | faMoon, 49 | faSun, 50 | faFilter, 51 | faGithub 52 | ); 53 | 54 | Vue.component("font-awesome-icon", FontAwesomeIcon); 55 | 56 | Vue.config.productionTip = false; 57 | 58 | new Vue({ 59 | router, 60 | store, 61 | render: (h) => h(App), 62 | }).$mount("#app"); 63 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | import LoginView from "../views/LoginView.vue"; 4 | import HomeView from "../views/HomeView.vue"; 5 | import TagView from "../views/TagView.vue"; 6 | import CategoryView from "../views/CategoryView.vue"; 7 | 8 | Vue.use(VueRouter); 9 | 10 | const routes = [ 11 | { 12 | path: "/", 13 | name: "home", 14 | component: HomeView, 15 | }, 16 | { 17 | path: "/latest", 18 | name: "latest", 19 | component: HomeView, 20 | }, 21 | { 22 | path: "/photo/:id", 23 | name: "photo", 24 | component: HomeView, 25 | }, 26 | { 27 | path: "/tag/:tag", 28 | name: "tag", 29 | component: TagView, 30 | }, 31 | { 32 | path: "/category/:category", 33 | name: "category", 34 | component: CategoryView, 35 | }, 36 | { 37 | path: "/login", 38 | name: "login", 39 | component: LoginView, 40 | }, 41 | ]; 42 | 43 | const router = new VueRouter({ 44 | mode: "history", 45 | routes, 46 | }); 47 | 48 | router.beforeEach((to, from, next) => { 49 | // // clear search if exists 50 | // if (store.state.searchText != "") { 51 | // store.state.searchText = ""; 52 | // } 53 | next(); 54 | }); 55 | 56 | export default router; 57 | -------------------------------------------------------------------------------- /src/store/app/actions.js: -------------------------------------------------------------------------------- 1 | import Axios from "axios"; 2 | 3 | let urlParameters = { 4 | q: "", 5 | imageType: "all", 6 | orientation: "all", 7 | category: null, 8 | colors: null, 9 | safesearch: "true", 10 | order: "popular", 11 | page: 1, 12 | perPage: 20, 13 | }; 14 | 15 | const urlGenerator = (newUrl) => { 16 | newUrl.forEach((item) => { 17 | const { type, value } = item; 18 | urlParameters = { 19 | ...urlParameters, 20 | [type]: value, 21 | }; 22 | }); 23 | 24 | let URL = 25 | `${process.env.VUE_APP_PHOTOS_URL}&order=${urlParameters.order}` + 26 | `&category=${urlParameters.category}&image_type=${urlParameters.imageType}` + 27 | `&orientation=${urlParameters.orientation}&colors=${urlParameters.colors}` + 28 | `&safesearch=${urlParameters.safesearch}` + 29 | `&per_page=${urlParameters.perPage}&page=${urlParameters.page}` + 30 | `&q=${urlParameters.q}`; 31 | 32 | return URL; 33 | }; 34 | 35 | export default { 36 | async fetchAll({ getters, commit }) { 37 | commit("setPhotosTimeline", null); 38 | try { 39 | const albums = []; 40 | await Promise.all([ 41 | getters.getCategories.forEach((element) => { 42 | Axios.get( 43 | `${urlGenerator([ 44 | { type: "perPage", value: 4 }, 45 | ])}&category=${element}` 46 | ).then(({ data }) => { 47 | const { hits } = data; 48 | const album = []; 49 | hits.forEach((item) => 50 | album.push({ [element]: { ...item, albumTitle: element } }) 51 | ); 52 | albums.push(album); 53 | commit("setAlbums", albums); 54 | }); 55 | }), 56 | Axios.get( 57 | urlGenerator([ 58 | { type: "order", value: "popular" }, 59 | { type: "perPage", value: 20 }, 60 | ]) 61 | ).then(({ data }) => { 62 | commit("setPhotosTimeline", data.hits); 63 | }), 64 | ]); 65 | } catch (error) { 66 | console.log(error); 67 | } 68 | }, 69 | async fetchPopular({ commit }) { 70 | commit("setPhotosTimeline", null); 71 | try { 72 | await Promise.all([ 73 | Axios.get( 74 | urlGenerator([ 75 | { type: "order", value: "popular" }, 76 | { type: "perPage", value: 20 }, 77 | { type: "page", value: 1 }, 78 | ]) 79 | ).then(({ data }) => { 80 | commit("setPhotosTimeline", data.hits); 81 | }), 82 | ]); 83 | } catch (error) { 84 | console.log(error); 85 | } 86 | }, 87 | async fetchLatest({ commit }) { 88 | commit("setPhotosTimeline", null); 89 | try { 90 | await Promise.all([ 91 | Axios.get( 92 | urlGenerator([ 93 | { type: "order", value: "latest" }, 94 | { type: "perPage", value: 20 }, 95 | { type: "page", value: 1 }, 96 | ]) 97 | ).then(({ data }) => { 98 | commit("setPhotosTimeline", data.hits); 99 | }), 100 | ]); 101 | } catch (error) { 102 | console.log(error); 103 | } 104 | }, 105 | async search({ commit }, payload) { 106 | commit("setPhotosTimeline", null); 107 | commit("setSearchText", payload); 108 | try { 109 | await Promise.all([ 110 | Axios.get( 111 | urlGenerator([ 112 | { type: "perPage", value: 20 }, 113 | { type: "q", value: encodeURIComponent(payload) }, 114 | ]) 115 | ).then(({ data }) => { 116 | if (data.hits) { 117 | if (data.hits.length > 0) { 118 | commit("setPhotosTimeline", data.hits); 119 | } else commit("setPhotosTimeline", "not-found"); 120 | } 121 | }), 122 | ]); 123 | } catch (error) { 124 | console.log(error); 125 | } 126 | }, 127 | async filter({ commit }, payload) { 128 | commit("setPhotosTimeline", null); 129 | try { 130 | await Promise.all([ 131 | Axios.get(urlGenerator(payload)).then(({ data }) => { 132 | if (data.hits) { 133 | if (data.hits.length > 0) { 134 | commit("setPhotosTimeline", data.hits); 135 | } else commit("setPhotosTimeline", null); 136 | } 137 | }), 138 | ]); 139 | } catch (error) { 140 | console.log(error); 141 | } 142 | }, 143 | async fetchMore({ commit }) { 144 | commit("setFetchStatus", true); 145 | try { 146 | await Promise.all([ 147 | Axios.get( 148 | urlGenerator([{ type: "page", value: urlParameters.page + 1 }]) 149 | ).then(({ data }) => { 150 | if (data.hits) { 151 | if (data.hits.length > 0) { 152 | commit("morePhotosTimeline", data.hits); 153 | } 154 | } 155 | }), 156 | ]); 157 | } catch (error) { 158 | console.log(error); 159 | } 160 | }, 161 | destroySearching({ commit, dispatch }) { 162 | commit("setSearchText", null); 163 | dispatch("filter", [{ type: "q", value: "" }]); 164 | }, 165 | resetCategory() { 166 | urlParameters.category = null; 167 | }, 168 | setdarkmode({ commit }, payload) { 169 | commit("darkmode", payload) 170 | } 171 | }; 172 | -------------------------------------------------------------------------------- /src/store/app/getters.js: -------------------------------------------------------------------------------- 1 | export default { 2 | getSignUpForm(state) { 3 | return state.signUpForm; 4 | }, 5 | getCategories(state) { 6 | return state.categories; 7 | }, 8 | getAlbums(state) { 9 | return state.albums; 10 | }, 11 | getPhotosTimeline(state) { 12 | return state.photosTimeline; 13 | }, 14 | getSearchText(state) { 15 | return state.searchText; 16 | }, 17 | getFilterSection(state) { 18 | return state.filterSection; 19 | }, 20 | getBackdrop(state) { 21 | return state.backdrop; 22 | }, 23 | getFetchStatus(state) { 24 | return state.fetchStatus; 25 | }, 26 | getDarkmode(state) { 27 | return state.darkmode; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/store/app/mutations.js: -------------------------------------------------------------------------------- 1 | export default { 2 | setUsername(state, payload) { 3 | state.signUpForm = { 4 | ...state.signUpForm, 5 | username: payload, 6 | }; 7 | }, 8 | setPassword(state, payload) { 9 | state.signUpForm = { 10 | ...state.signUpForm, 11 | password: payload, 12 | }; 13 | }, 14 | setAlbums(state, payload) { 15 | state.albums = payload; 16 | }, 17 | setPhotosTimeline(state, payload) { 18 | state.photosTimeline = payload; 19 | }, 20 | morePhotosTimeline(state, payload) { 21 | state.photosTimeline = [...state.photosTimeline, ...payload]; 22 | }, 23 | setSearchText(state, payload) { 24 | state.searchText = payload; 25 | }, 26 | toggleFilterSection(state) { 27 | state.filterSection = !state.filterSection; 28 | }, 29 | toggleBackdrop(state) { 30 | state.backdrop = !state.backdrop; 31 | }, 32 | setFetchStatus(state, payload) { 33 | state.fetchStatus = payload 34 | }, 35 | darkmode(state, payload) { 36 | state.darkmode = payload 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | import getters from "./app/getters"; 4 | import mutations from "./app/mutations"; 5 | import actions from "./app/actions"; 6 | 7 | Vue.use(Vuex); 8 | 9 | export default new Vuex.Store({ 10 | state: { 11 | signUpForm: { 12 | username: "", 13 | password: "", 14 | }, 15 | albums: [], 16 | photosTimeline: [], 17 | searchText: "", 18 | filterSection: false, 19 | backdrop: false, 20 | fetchStatus: false, 21 | darkmode: false, 22 | categories: [ 23 | "backgrounds", 24 | "fashion", 25 | "nature", 26 | "science", 27 | "people", 28 | "places", 29 | "animals", 30 | "industry", 31 | "computer", 32 | "sports", 33 | "buildings", 34 | "health", 35 | "travel", 36 | "music", 37 | ], 38 | }, 39 | getters, 40 | mutations, 41 | actions, 42 | modules: { 43 | // const moduleA = { 44 | // state: () => ({ ... }), 45 | // mutations: { ... }, 46 | // actions: { ... }, 47 | // getters: { ... } 48 | // } 49 | // const moduleB = { 50 | // state: () => ({ ... }), 51 | // mutations: { ... }, 52 | // actions: { ... } 53 | // } 54 | // const store = createStore({ 55 | // modules: { 56 | // a: moduleA, 57 | // b: moduleB 58 | // } 59 | // }) 60 | // store.state.a // -> `moduleA`'s state 61 | // store.state.b // -> `moduleB`'s state 62 | }, 63 | }); 64 | -------------------------------------------------------------------------------- /src/views/CategoryView.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 128 | 129 | 148 | -------------------------------------------------------------------------------- /src/views/HomeView.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 147 | 148 | 163 | -------------------------------------------------------------------------------- /src/views/LoginView.vue: -------------------------------------------------------------------------------- 1 | 80 | 81 | 105 | 106 | 217 | -------------------------------------------------------------------------------- /src/views/TagView.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 129 | 130 | 145 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | module.exports = defineConfig({ 3 | transpileDependencies: true 4 | }) 5 | --------------------------------------------------------------------------------