├── .npmrc
├── .prettierrc
├── request
├── urls
│ ├── reply.js
│ ├── category.js
│ ├── article.js
│ ├── comment.js
│ └── user.js
├── api
│ ├── reply.js
│ ├── category.js
│ ├── comment.js
│ ├── article.js
│ └── user.js
├── request.js
└── http.js
├── boblog.png
├── static
└── favicon.ico
├── plugins
├── axios-ports.js
├── route.js
├── md.js
├── element-ui.js
├── axios.js
└── scrollTo.js
├── .env.development
├── .env.production
├── layouts
├── hutao.vue
└── default.vue
├── README.md
├── .editorconfig
├── .eslintrc.js
├── lib
├── auth.js
├── utils.js
├── hutao.js
└── progress-indicator.js
├── store
├── category.js
└── user.js
├── components
├── common
│ ├── Footer.vue
│ ├── Header.vue
│ └── LoginForm.vue
└── article
│ └── ArticleComment.vue
├── package.json
├── .gitignore
├── nuxt.config.js
├── pages
├── user.vue
├── usercenter.vue
├── article.vue
├── index.vue
└── hutao.vue
└── assets
└── css
└── common.css
/.npmrc:
--------------------------------------------------------------------------------
1 | registry = https://registry.npmmirror.com
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true
4 | }
5 |
--------------------------------------------------------------------------------
/request/urls/reply.js:
--------------------------------------------------------------------------------
1 | export default {
2 | create: '/reply'
3 | }
4 |
--------------------------------------------------------------------------------
/boblog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lfb/nuxtjs-blog-web/HEAD/boblog.png
--------------------------------------------------------------------------------
/request/urls/category.js:
--------------------------------------------------------------------------------
1 | export default {
2 | list: '/category',
3 | }
4 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lfb/nuxtjs-blog-web/HEAD/static/favicon.ico
--------------------------------------------------------------------------------
/request/urls/article.js:
--------------------------------------------------------------------------------
1 | export default {
2 | list: '/article',
3 | detail: '/article',
4 | }
5 |
--------------------------------------------------------------------------------
/request/urls/comment.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 | create: '/comment',
4 | target: '/comment/target/list',
5 | }
6 |
--------------------------------------------------------------------------------
/plugins/axios-ports.js:
--------------------------------------------------------------------------------
1 | import { setClient } from '~/request/request'
2 |
3 | export default ({ app, store }) => {
4 | setClient(app.$axios)
5 | }
6 |
--------------------------------------------------------------------------------
/request/urls/user.js:
--------------------------------------------------------------------------------
1 | export default {
2 | login: '/user/login',
3 | register: '/user/register',
4 | auth: '/user/auth',
5 | list: '/user/list'
6 | }
7 |
--------------------------------------------------------------------------------
/.env.development:
--------------------------------------------------------------------------------
1 | # just a flag
2 | NUXT_APP_ENV = 'development'
3 |
4 | # base api
5 | BASE_URL = 'http://localhost:5000/api/v1'
6 | BOBLOG_TOKEN = 'BOBLOG_TOKEN'
7 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | # just a flag
2 | NUXT_APP_ENV = 'production'
3 |
4 | # base api
5 | BASE_URL = 'https://api.boblog.com/api/v1'
6 | BOBLOG_TOKEN = 'BOBLOG_TOKEN'
7 |
8 |
--------------------------------------------------------------------------------
/layouts/hutao.vue:
--------------------------------------------------------------------------------
1 |
2 |
' +
10 | hljs.highlight(str, {
11 | language: lang,
12 | ignoreIllegals: true,
13 | }).value +
14 | ''
15 | )
16 | } catch (__) { }
17 | }
18 |
19 | return (
20 | '' + md.utils.escapeHtml(str) + ''
21 | )
22 | },
23 | })
24 |
25 | Vue.prototype.$md = md;
26 | export default md;
27 |
--------------------------------------------------------------------------------
/store/category.js:
--------------------------------------------------------------------------------
1 | import { getCategory } from '@/request/api/category'
2 | const state = () => ({
3 | categoryList: [],
4 | })
5 |
6 | const mutations = {
7 | SET_CATEGORY_LIST(state, data) {
8 | state.categoryList = data
9 | },
10 |
11 | }
12 |
13 | const actions = {
14 | async getCategoryData({ state, commit }, params = {}) {
15 | if (Array.isArray(state.categoryList) && state.categoryList.length > 0) {
16 | return state.categoryList
17 | } else {
18 | const [err, res] = await getCategory(params)
19 | if (!err) {
20 | const category = res.data.data.data
21 | commit('SET_CATEGORY_LIST', category)
22 | return category
23 | } else {
24 | return err
25 | }
26 | }
27 | },
28 | }
29 |
30 | export default {
31 | namespace: true,
32 | state,
33 | actions,
34 | mutations
35 | }
36 |
--------------------------------------------------------------------------------
/plugins/element-ui.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import {
3 | Icon,
4 | Drawer,
5 | Dialog,
6 | Button,
7 | Avatar,
8 | Dropdown,
9 | DropdownMenu,
10 | DropdownItem,
11 | Pagination,
12 | Input,
13 | Loading,
14 | MessageBox,
15 | Message,
16 | } from 'element-ui'
17 |
18 | import locale from 'element-ui/lib/locale/lang/en'
19 |
20 | const components = [
21 | Icon,
22 | Button,
23 | Avatar,
24 | Dialog,
25 | Dropdown,
26 | DropdownMenu,
27 | DropdownItem,
28 | Pagination,
29 | Input,
30 | Drawer
31 | ]
32 | const Element = {
33 | install(Vue) {
34 | components.forEach(component => {
35 | Vue.component(component.name, component)
36 | })
37 | }
38 | }
39 |
40 | Vue.use(Loading.directive);
41 | Vue.prototype.$loading = Loading.service;
42 | Vue.prototype.$msgbox = MessageBox;
43 | Vue.prototype.$alert = MessageBox.alert;
44 | Vue.prototype.$confirm = MessageBox.confirm;
45 | Vue.prototype.$prompt = MessageBox.prompt;
46 | Vue.prototype.$message = Message;
47 |
48 | Vue.use(Element, { locale })
49 |
--------------------------------------------------------------------------------
/plugins/axios.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { removeToken, encodeToken } from "@/lib/auth";
3 |
4 | export default ({ $axios, store }) => {
5 | $axios.onRequest(config => {
6 | config.baseURL = process.env.BASE_URL
7 | config.headers.Authorization = encodeToken()
8 |
9 | return config
10 | })
11 |
12 | $axios.onResponse(res => {
13 | if (res.status === 200 && res.data.code === 200) {
14 | return res
15 | } else {
16 | Vue.prototype.$message.error(res.data.msg || '获取失败')
17 | return Promise.reject(res)
18 | }
19 | })
20 |
21 | $axios.onError(err => {
22 | const { response } = err || {}
23 |
24 | // 处理token过期无效情况,清除token,初始化store数据
25 | if ([401, 403].includes(response?.status)) {
26 | removeToken()
27 | store.commit('user/SET_LOGIN_STATUS', false)
28 | store.commit('user/SET_USERINFO', null)
29 | }
30 |
31 | const msg = Array.isArray(response?.data.msg) ? response?.data?.msg.join(',') : response?.data?.msg
32 | Vue.prototype.$message.error(msg || '获取失败')
33 | return Promise.reject(err)
34 | })
35 | }
36 |
--------------------------------------------------------------------------------
/components/common/Footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
24 |
25 |
56 |
--------------------------------------------------------------------------------
/request/http.js:
--------------------------------------------------------------------------------
1 | import service from '~/request/request.js'
2 |
3 | export function GET(config) {
4 | const { url = '', data = {}, ...opt } = config
5 | return service
6 | .get(url, {
7 | params: data,
8 | ...opt
9 | })
10 | .then(res => {
11 | return [null, res]
12 | })
13 | .catch(err => {
14 |
15 | return [err, null]
16 | })
17 | }
18 | export function POST(config) {
19 | const { url = '', data = {}, ...opt } = config
20 | return service
21 | .post(url, data, opt)
22 | .then(res => {
23 | return [null, res]
24 | })
25 | .catch(err => {
26 | return [err, null]
27 | })
28 | }
29 |
30 | export function UPLOAD(config) {
31 | const { url = '', data = {}, ...opt } = config
32 | return service
33 | .post(url, data, {
34 | headers: {
35 | 'Content-Type': 'multipart/form-data'
36 | },
37 | ...opt
38 | })
39 | .then(res => {
40 | return [null, res]
41 | })
42 | .catch(err => {
43 | return [err, null]
44 | })
45 | }
46 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "1.0.0",
4 | "private": true,
5 | "config": {
6 | "nuxt": {
7 | "host": "localhost",
8 | "port": "3000"
9 | }
10 | },
11 | "scripts": {
12 | "dev": "cross-env NODE_ENV=development nuxt",
13 | "build": "nuxt build",
14 | "start": "nuxt start",
15 | "generate": "nuxt generate",
16 | "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
17 | "lint": "yarn lint:js"
18 | },
19 | "dependencies": {
20 | "@nuxtjs/axios": "^5.13.6",
21 | "@xunlei/vue-lazy-component": "^1.1.3",
22 | "babel-plugin-component": "^1.1.1",
23 | "cookie-universal-nuxt": "^2.1.5",
24 | "core-js": "^3.15.1",
25 | "cross-env": "^7.0.3",
26 | "dotenv": "^10.0.0",
27 | "element-ui": "^2.15.2",
28 | "highlight.js": "^11.1.0",
29 | "js-base64": "^3.6.1",
30 | "js-cookie": "^2.2.1",
31 | "markdown-it": "^12.1.0",
32 | "node-sass": "^6.0.1",
33 | "nuxt": "^2.15.7",
34 | "sass-loader": "^10.1.1"
35 | },
36 | "devDependencies": {
37 | "@babel/eslint-parser": "^7.14.7",
38 | "@nuxtjs/eslint-config": "^6.0.1",
39 | "@nuxtjs/eslint-module": "^3.0.2",
40 | "eslint": "^7.29.0",
41 | "eslint-config-prettier": "^8.3.0",
42 | "eslint-plugin-nuxt": "^2.0.0",
43 | "eslint-plugin-vue": "^7.12.1",
44 | "prettier": "^2.3.2"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/hutao.js:
--------------------------------------------------------------------------------
1 | export const LEVEL_TEXT = {
2 | SSS: {
3 | level: 'SSS',
4 | color: '#ff9900',
5 | star: 5,
6 | text: '胡桃厨天花板级圣遗物,又称遗物级圣遗物,恭喜你,你的有效词条数已经接近满值,增伤点数极限为 5。'
7 | },
8 | SS: {
9 | level: 'SS',
10 | color: '#19be6b',
11 | star: 4,
12 | text: '极品圣遗物,欧皇专属,很给力的圣遗物,3 的增伤值达标,下一阶段需要 4 的增伤值。'
13 | },
14 | S: {
15 | level: 'S',
16 | color: '#5cadff',
17 | star: 3,
18 | text: '毕业圣遗物,如果你不是胡桃厨可以休息啦!2 的增伤值达标,下一阶段需要 3 的增伤值。'
19 | },
20 | C: {
21 | level: 'C',
22 | star: 2,
23 | color: '#808695',
24 | text: '赶快为你的老婆多刷点圣遗物吧,都快穷的吃土了,下一阶段需要 2 的增伤值。'
25 | }
26 | }
27 |
28 | export const ATTRIBUTES_MAP = {
29 | JING_TONG: 23.31,
30 | SHENG_MING_BAI_FEN_BI: 5.83,
31 | XIAO_SHENG_MING: 906,
32 | BAO_SHANG: 7.77,
33 | GONG_JI_BAI_FEN_BI: 5.83,
34 | XIAO_GONG_JI: 41
35 | }
36 |
37 | export const equipArray = [{
38 | key: 'JING_TONG',
39 | name: '精通',
40 | value: '',
41 | placeholder: '精通'
42 | },
43 | {
44 | key: 'BAO_SHANG',
45 | name: '爆伤',
46 | value: '',
47 | placeholder: '暴击伤害'
48 | },
49 | {
50 | key: 'SHENG_MING_BAI_FEN_BI',
51 | name: '大生命',
52 | value: '',
53 | placeholder: '生命百分比'
54 | },
55 | {
56 | key: 'XIAO_SHENG_MING',
57 | name: '小生命',
58 | value: '',
59 | placeholder: '小生命'
60 | },
61 | {
62 | key: 'GONG_JI_BAI_FEN_BI',
63 | name: '大攻击',
64 | value: '',
65 | placeholder: '攻击百分比',
66 | isUndivided: true
67 | },
68 | {
69 | key: 'XIAO_GONG_JI',
70 | name: '小攻击',
71 | value: '',
72 | placeholder: '小攻击',
73 | isUndivided: true
74 | }
75 | ]
76 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/nuxt.config.js:
--------------------------------------------------------------------------------
1 | const dotenv = require('dotenv')
2 | const envConfig = dotenv.config({ path: `.env.${process.env.NODE_ENV}` }).parsed
3 |
4 | export default {
5 | // Global page headers: https://go.nuxtjs.dev/config-head
6 | head: {
7 | title: 'frontend',
8 | htmlAttrs: {
9 | lang: 'en'
10 | },
11 | meta: [
12 | { charset: 'utf-8' },
13 | { name: 'viewport', content: 'width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0' },
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 | '@/assets/css/common.css',
25 | 'element-ui/lib/theme-chalk/index.css'
26 | ],
27 |
28 | // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
29 | plugins: [
30 | '@/plugins/axios',
31 | '@/plugins/axios-ports',
32 | '@/plugins/md',
33 | '@/plugins/route',
34 | { src: '@/plugins/scrollTo', mode: 'client' },
35 | '@/plugins/element-ui'
36 | ],
37 |
38 | // Auto import components: https://go.nuxtjs.dev/config-components
39 | // components: true,
40 |
41 | // Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
42 | buildModules: [
43 | // https://go.nuxtjs.dev/eslint
44 | // '@nuxtjs/eslint-module',
45 | ],
46 |
47 | // Modules: https://go.nuxtjs.dev/config-modules
48 | modules: [
49 | '@nuxtjs/axios',
50 | 'cookie-universal-nuxt',
51 | ['cookie-universal-nuxt', { alias: 'cookiz' }],
52 | ],
53 | env: envConfig,
54 | // Build Configuration: https://go.nuxtjs.dev/config-build
55 | build: {
56 | transpile: [/^element-ui/],
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/store/user.js:
--------------------------------------------------------------------------------
1 | import { login, register, info } from '@/request/api/user'
2 | import { setToken } from "@/lib/auth";
3 |
4 | const state = () => ({
5 | userInfo: null,
6 | isLoginStatus: false
7 | })
8 |
9 | const mutations = {
10 | SET_USERINFO(state, data) {
11 | state.userInfo = data
12 | },
13 | SET_LOGIN_STATUS(state, data) {
14 | state.isLoginStatus = data
15 | }
16 | }
17 |
18 | const actions = {
19 | async userLogin({ state, commit }, params = {}) {
20 | const [err, res] = await login(params)
21 | if (!err) {
22 | const user = res.data.data
23 | commit('SET_USERINFO', {
24 | id: user.id,
25 | username: user.username,
26 | email: user.email
27 | })
28 | commit('SET_LOGIN_STATUS', true)
29 | setToken(user.token)
30 | return [null, user]
31 | } else {
32 | return [err, null]
33 | }
34 | },
35 | async userRegister({ state, commit }, params = {}) {
36 | const [err, res] = await register(params)
37 | if (!err) {
38 | const user = res.data.data
39 | commit('SET_USERINFO', {
40 | id: user.id,
41 | username: user.username,
42 | email: user.email
43 | })
44 | commit('SET_LOGIN_STATUS', true)
45 | setToken(user.token)
46 | return [null, user]
47 | } else {
48 | return [err, null]
49 | }
50 |
51 | },
52 | async userInfo({ state, commit }, params = {}) {
53 | if (state.isLoginStatus && state.userInfo) {
54 | return state.userInfo
55 | }
56 |
57 | const [err, res] = await info(params)
58 | if (!err) {
59 | const user = res.data.data
60 | commit('SET_USERINFO', {
61 | id: user.id,
62 | username: user.username,
63 | email: user.email
64 | })
65 | commit('SET_LOGIN_STATUS', true)
66 | return [null, user]
67 | } else {
68 | return [err, null]
69 | }
70 | },
71 | }
72 |
73 | export default {
74 | namespace: true,
75 | state,
76 | actions,
77 | mutations
78 | }
79 |
--------------------------------------------------------------------------------
/plugins/scrollTo.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | Math.easeInOutQuad = function (t, b, c, d) {
4 | // eslint-disable-next-line no-param-reassign
5 | t /= d / 2
6 | if (t < 1) {
7 | return (c / 2) * t * t + b
8 | }
9 | // eslint-disable-next-line no-param-reassign
10 | t--
11 | return (-c / 2) * (t * (t - 2) - 1) + b
12 | }
13 |
14 | // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
15 | const requestAnimFrame = (function () {
16 | return (
17 | window.requestAnimationFrame ||
18 | window.webkitRequestAnimationFrame ||
19 | window.mozRequestAnimationFrame ||
20 | function (callback) {
21 | window.setTimeout(callback, 1000 / 60)
22 | }
23 | )
24 | })()
25 |
26 | // because it's so fucking difficult to detect the scrolling element, just move them all
27 | function move(amount) {
28 | document.documentElement.scrollTop = amount
29 | document.body.parentNode.scrollTop = amount
30 | document.body.scrollTop = amount
31 | }
32 |
33 | function position() {
34 | return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
35 | }
36 |
37 | function scrollTo(to, duration, callback) {
38 | const start = position()
39 | const change = to - start
40 | const increment = 20
41 | let currentTime = 0
42 | // eslint-disable-next-line no-param-reassign
43 | duration = typeof duration === 'undefined' ? 500 : duration
44 | const animateScroll = function () {
45 | // increment the time
46 | currentTime += increment
47 | // find the value with the quadratic in-out easing function
48 | const val = Math.easeInOutQuad(currentTime, start, change, duration)
49 | // move the document.body
50 | move(val)
51 | // do the animation unless its over
52 | if (currentTime < duration) {
53 | requestAnimFrame(animateScroll)
54 | } else if (callback && typeof callback === 'function') {
55 | // the animation is done so lets callback
56 | callback()
57 | }
58 | }
59 | animateScroll()
60 | }
61 |
62 |
63 | Vue.prototype.$scrollTo = scrollTo
64 |
65 | export default scrollTo
66 |
--------------------------------------------------------------------------------
/pages/user.vue:
--------------------------------------------------------------------------------
1 |
2 | 昵称:{{ userInfo.username }}
6 |邮箱:{{ userInfo.email }}
7 |8 | —— 假如生活欺骗了你,请你不要放弃,坚持下去!天是不会给绝路你的! 9 |
10 | 11 |昵称:{{ userInfo.username }}
5 |邮箱:{{ userInfo.email }}
6 |7 | —— 假如生活欺骗了你,请你不要放弃,坚持下去!天是不会给绝路你的! 8 |
9 |文章:{{ item.article.title }}
19 |评论内容:{{ item.content }}
20 |评论时间:{{ item.created_at }}
21 |回复:{{ item.reply_list || '无' }}
22 |
37 |
6 |
10 | {{errorText}}
24 |{{estimateText.text}}
25 |
46 | 平均增伤值>4,裸蒸发6万多
67 |平均增伤值>5,裸蒸发7万多
68 |平均增伤值>6,裸蒸发8万多
69 | 70 | 爆伤头(精通/生命百分比): 71 |平均增伤值>2.4,裸蒸发6万多
72 |平均增伤值>3.4,裸蒸发7万多
73 |平均增伤值>4.4,裸蒸发8万多
74 |
80 |
83 |
文章:{{ item.article.title }}
19 |评论内容:{{ item.content }}
20 |评论时间:{{ item.created_at }}
21 |回复:{{ item.reply_list || '无' }}
22 |