├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── components ├── Footer.vue ├── Header.vue ├── ListView.vue └── NuxtLogo.vue ├── dummy ├── chunk.js └── definedata.js ├── jsconfig.json ├── layouts └── no_use_default.vue ├── nuxt.config.js ├── package.json ├── pages ├── boardlist.vue └── index.vue ├── static └── favicon.ico └── store ├── README.md └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | # macOS 87 | .DS_Store 88 | 89 | # Vim swap files 90 | *.swp 91 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 dev. ahnshy 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 | # nuxt3-bbs 2 | 3 | ## Build Setup 4 | 5 | ```bash 6 | # install dependencies 7 | $ npm install 8 | 9 | # serve with hot reload at localhost:3000 10 | $ npm run dev 11 | 12 | # build for production and launch server 13 | $ npm run build 14 | $ npm run start 15 | 16 | # generate static project 17 | $ npm run generate 18 | ``` 19 | 20 | For detailed explanation on how things work, check out the [documentation](https://nuxtjs.org). 21 | 22 | ## Special Directories 23 | 24 | You can create the following extra directories, some of which have special behaviors. Only `pages` is required; you can delete them if you don't want to use their functionality. 25 | 26 | ### `assets` 27 | 28 | The assets directory contains your uncompiled assets such as Stylus or Sass files, images, or fonts. 29 | 30 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/assets). 31 | 32 | ### `components` 33 | 34 | The components directory contains your Vue.js components. Components make up the different parts of your page and can be reused and imported into your pages, layouts and even other components. 35 | 36 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/components). 37 | 38 | ### `layouts` 39 | 40 | Layouts are a great help when you want to change the look and feel of your Nuxt app, whether you want to include a sidebar or have distinct layouts for mobile and desktop. 41 | 42 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/layouts). 43 | 44 | 45 | ### `pages` 46 | 47 | This directory contains your application views and routes. Nuxt will read all the `*.vue` files inside this directory and setup Vue Router automatically. 48 | 49 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/get-started/routing). 50 | 51 | ### `plugins` 52 | 53 | The plugins directory contains JavaScript plugins that you want to run before instantiating the root Vue.js Application. This is the place to add Vue plugins and to inject functions or constants. Every time you need to use `Vue.use()`, you should create a file in `plugins/` and add its path to plugins in `nuxt.config.js`. 54 | 55 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/plugins). 56 | 57 | ### `static` 58 | 59 | This directory contains your static files. Each file inside this directory is mapped to `/`. 60 | 61 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 62 | 63 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/static). 64 | 65 | ### `store` 66 | 67 | This directory contains your Vuex store files. Creating a file in this directory automatically activates Vuex. 68 | 69 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/store). 70 | -------------------------------------------------------------------------------- /components/Footer.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 40 | -------------------------------------------------------------------------------- /components/Header.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 33 | -------------------------------------------------------------------------------- /components/ListView.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 104 | -------------------------------------------------------------------------------- /components/NuxtLogo.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /dummy/chunk.js: -------------------------------------------------------------------------------- 1 | const chunk = (array, size) => { 2 | size = Math.max(size, 0) 3 | const length = array == null ? 0 : array.length 4 | if (!length || size < 1) { 5 | return [] 6 | } 7 | let index = 0 8 | let resIndex = 0 9 | const result = new Array(Math.ceil(length / size)) 10 | 11 | while (index < length) { 12 | result[resIndex++] = array.slice(index, (index += size)) 13 | } 14 | return result 15 | } 16 | 17 | export default chunk 18 | -------------------------------------------------------------------------------- /dummy/definedata.js: -------------------------------------------------------------------------------- 1 | const formatDate = d => { 2 | const date = new Date(d) 3 | const monthNames = [ 4 | "01", 5 | "02", 6 | "03", 7 | "04", 8 | "05", 9 | "06", 10 | "07", 11 | "08", 12 | "09", 13 | "10", 14 | "11", 15 | "12" 16 | ] 17 | 18 | const day = date.getDate() 19 | const monthIndex = date.getMonth() 20 | const year = date.getFullYear() 21 | return `${day}/${monthNames[monthIndex]}/${year}` 22 | } 23 | 24 | export { formatDate } 25 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "~/*": ["./*"], 6 | "@/*": ["./*"], 7 | "~~/*": ["./*"], 8 | "@@/*": ["./*"] 9 | } 10 | }, 11 | "exclude": ["node_modules", ".nuxt", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /layouts/no_use_default.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode 3 | ssr: false, 4 | 5 | // Global page headers: https://go.nuxtjs.dev/config-head 6 | head: { 7 | title: 'nuxt3-bbs', 8 | htmlAttrs: { 9 | lang: 'en' 10 | }, 11 | meta: [ 12 | { charset: 'utf-8' }, 13 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 14 | { hid: 'description', name: 'description', content: '' }, 15 | { name: 'format-detection', content: 'telephone=no' } 16 | ], 17 | link: [ 18 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } 19 | ] 20 | }, 21 | 22 | // Global CSS: https://go.nuxtjs.dev/config-css 23 | css: [ 24 | ], 25 | 26 | // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins 27 | plugins: [ 28 | ], 29 | 30 | // Auto import components: https://go.nuxtjs.dev/config-components 31 | components: true, 32 | 33 | // Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules 34 | buildModules: [ 35 | ], 36 | 37 | // Modules: https://go.nuxtjs.dev/config-modules 38 | modules: [ 39 | '@nuxtjs/tailwindcss' 40 | ], 41 | 42 | // Build Configuration: https://go.nuxtjs.dev/config-build 43 | build: { 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt3-bbs", 3 | "version": "1.0.0", 4 | "author": "ahnshy ", 5 | "private": true, 6 | "scripts": { 7 | "dev": "nuxt", 8 | "build": "nuxt build", 9 | "start": "nuxt start", 10 | "generate": "nuxt generate" 11 | }, 12 | "dependencies": { 13 | "axios": "^1.7.7", 14 | "core-js": "^3.25.3", 15 | "nuxt": "^2.15.8", 16 | "vue": "^2.7.10", 17 | "vue-server-renderer": "^2.7.10", 18 | "vue-template-compiler": "^2.7.10" 19 | }, 20 | "devDependencies": { 21 | "@nuxtjs/tailwindcss": "^6.12.1", 22 | "autoprefixer": "^10.4.20", 23 | "postcss": "^8.4.41", 24 | "tailwindcss": "^3.4.10" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pages/boardlist.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahnshy/nuxt3-bbs/857ecfd4c3fff5cca554ca3dcfc9af45e84bbd91/static/favicon.ico -------------------------------------------------------------------------------- /store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). 11 | -------------------------------------------------------------------------------- /store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue" 2 | import Vuex from "vuex" 3 | import axios from "axios" 4 | Vue.use(Vuex) 5 | 6 | const API_URL = "http://localhost:3000/api" 7 | 8 | const createStore = () => { 9 | return new Vuex.Store({ 10 | state: { 11 | articleList: [], 12 | pageCount: null, 13 | detailArticle: {}, 14 | showErrModal: false, 15 | serverErr: null, 16 | locales: ["en", "vn"], 17 | locale: "en" 18 | }, 19 | actions: { 20 | LOAD_ARTICLE_LIST: async function( 21 | { commit }, 22 | query = { page: 1, limit: 5 } 23 | ) { 24 | try { 25 | const { data } = await axios.get( 26 | `${API_URL}/articles?page=${query.page}&limit=${query.limit}` 27 | ) 28 | commit("SET_ARTICLE_LIST", { list: data }) 29 | } catch (err) { 30 | commit("SET_SHOW_ERR", "Cannot get Article list") 31 | } 32 | }, 33 | LOAD_AN_ARTICLE: async function({ commit }, id) { 34 | try { 35 | const { data } = await axios.get(`${API_URL}/article/${id}`) 36 | commit("SET_DETAIL_ARTICLE", data) 37 | } catch (err) { 38 | commit("SET_SHOW_ERR", `Cannot get Article with id ${id}`) 39 | } 40 | }, 41 | POST_NEW_ARTICLE: function({ commit, dispatch }, data) { 42 | console.warn(data) 43 | axios 44 | .post(`${API_URL}/article`, data) 45 | .then(() => { 46 | dispatch("LOAD_ARTICLE_LIST") 47 | }) 48 | .catch(() => { 49 | commit("SET_SHOW_ERR", `Cannot create new article`) 50 | }) 51 | }, 52 | UPDATE_AN_ARTICLE: function({ commit, dispatch }, data) { 53 | axios 54 | .put(`${API_URL}/article/${data._id}`, data) 55 | .then(() => { 56 | dispatch("LOAD_ARTICLE_LIST") 57 | }) 58 | .catch(() => { 59 | commit("SET_SHOW_ERR", `Cannot edit article id ${data._id}`) 60 | }) 61 | }, 62 | DELETE_AN_ARTICLE: function({ commit, dispatch }, id) { 63 | axios 64 | .delete(`${API_URL}/article/${id}`) 65 | .then(() => { 66 | dispatch("LOAD_ARTICLE_LIST") 67 | }) 68 | .catch(() => { 69 | commit("SET_SHOW_ERR", `Cannot delete article id ${id}`) 70 | }) 71 | }, 72 | SET_VIEW_ARTICLE: function({ commit }, id) { 73 | commit("UPDATE_VIEW_ARTICLE", id) 74 | }, 75 | SET_HIDE_ERR: function({ commit }) { 76 | commit("SET_HIDE_ERR") 77 | } 78 | }, 79 | mutations: { 80 | SET_ARTICLE_LIST: (state, { list }) => { 81 | state.articleList = list.data 82 | state.pageCount = list.pageCount 83 | }, 84 | SET_DETAIL_ARTICLE: (state, article) => { 85 | state.detailArticle = article 86 | }, 87 | SET_SHOW_ERR: (state, text) => { 88 | state.showErrModal = true 89 | state.serverErr = text 90 | }, 91 | SET_HIDE_ERR: state => { 92 | state.showErrModal = false 93 | state.serverErr = null 94 | }, 95 | SET_LANG(state, locale) { 96 | if (state.locales.indexOf(locale) !== -1) { 97 | state.locale = locale 98 | } 99 | } 100 | }, 101 | getters: { 102 | articleList: state => { 103 | return state.articleList 104 | }, 105 | totalPage: state => { 106 | return state.pageCount 107 | }, 108 | detailArticle: state => { 109 | return state.detailArticle 110 | }, 111 | isServerErr: state => state.showErrModal, 112 | serverErrMessage: state => state.serverErr 113 | } 114 | }) 115 | } 116 | 117 | export default createStore 118 | --------------------------------------------------------------------------------