├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .huskyrc.js ├── .postcssrc.js ├── .prettierrc ├── README.md ├── index.html ├── lint-staged.config.js ├── package.json ├── pnpm-lock.yaml ├── public └── favicon.ico ├── src ├── App.vue ├── api │ ├── apis.js │ ├── auth.js │ ├── blog.js │ ├── http.js │ └── index.js ├── assets │ ├── images │ │ └── background.png │ ├── logo.png │ └── styles │ │ ├── _mixins.scss │ │ ├── _typography.scss │ │ ├── _utilities.scss │ │ ├── _variables.scss │ │ ├── card.scss │ │ ├── common │ │ ├── transition.scss │ │ └── var.scss │ │ ├── global.scss │ │ ├── index.scss │ │ ├── mixins │ │ ├── _breakpoints.scss │ │ ├── _clearfix.scss │ │ ├── _float.scss │ │ ├── _hover.scss │ │ ├── _lists.scss │ │ ├── _text-truncate.scss │ │ └── cards.scss │ │ ├── pages.scss │ │ ├── reset.scss │ │ ├── theme-future-reboot.scss │ │ ├── theme-future.scss │ │ └── utilities │ │ ├── _align.scss │ │ ├── _background.scss │ │ ├── _border.scss │ │ └── _text.scss ├── components │ ├── Charts │ │ └── G2Mixin.ts │ └── Common │ │ ├── ArticleCard.vue │ │ ├── ArticleSummaryCard.vue │ │ ├── CircleText.vue │ │ ├── Pagination.vue │ │ └── VueMarkdown.vue ├── directives │ └── index.js ├── filters │ └── index.js ├── main.js ├── pages │ ├── About.vue │ ├── About │ │ ├── BasicInfo.vue │ │ ├── HobbyInfo.vue │ │ ├── HobbyProjectInfo.vue │ │ ├── ProjectInfo.vue │ │ ├── SkillInfo.vue │ │ └── WorkInfo.vue │ ├── AboutPageComponents │ │ ├── ProgrammingLanguageUsage.vue │ │ ├── TechUsage.vue │ │ └── TestPie.vue │ ├── Archive.vue │ ├── Blog.vue │ ├── Blog │ │ └── ArticlePost.vue │ ├── Gallery.vue │ ├── Home.vue │ ├── Works.vue │ └── commons │ │ ├── CategoryCard.vue │ │ ├── Footer.vue │ │ ├── Header.vue │ │ ├── ProfileCard.vue │ │ ├── PromotionCard.vue │ │ └── TagsCard.vue ├── routes.js └── store │ ├── actions.js │ ├── getters.js │ ├── index.js │ └── mutations.js ├── static ├── .gitkeep ├── hightlight │ ├── solarized-dark.css │ └── solarized-light.css ├── iconfont │ ├── .gitkeep │ ├── iconfont.css │ ├── iconfont.eot │ ├── iconfont.svg │ ├── iconfont.ttf │ └── iconfont.woff ├── images │ ├── bg_terminal.jpg │ ├── mp_wechat.jpg │ ├── shoes.jpg │ └── tips_wechat.jpeg └── js │ └── smooth-scroll.min.js ├── test ├── e2e │ ├── custom-assertions │ │ └── elementCount.js │ ├── nightwatch.conf.js │ ├── runner.js │ └── specs │ │ └── test.js └── unit │ ├── .eslintrc │ ├── jest.conf.js │ ├── setup.js │ └── specs │ └── HelloWorld.spec.js └── vite.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | // add more generic rulesets here, such as: 4 | // 'eslint:recommended', 5 | 'plugin:vue/vue3-recommended', 6 | // 'plugin:vue/recommended' // Use this if you are using Vue.js 2.x. 7 | ], 8 | rules: { 9 | // override/add rules settings here, such as: 10 | // 'vue/no-unused-vars': 'error' 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | /test/unit/coverage/ 8 | /test/e2e/reports/ 9 | selenium-debug.log 10 | 11 | # 确保不提交编译生成的文件 12 | /packages/theme-future/lib 13 | /src/assets/theme-future/ 14 | /src/assets/theme-future-scss/ 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | -------------------------------------------------------------------------------- /.huskyrc.js: -------------------------------------------------------------------------------- 1 | // .huskyrc.js 2 | module.exports = { 3 | hooks: { 4 | // git commit 前的钩子 5 | "pre-commit": "lint-staged", 6 | "post-commit": "git update-index --again", 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": false, 4 | "trailingComma": "es5", 5 | "tabWidth": 2 6 | } 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ya Vue Blog 2 | 3 | - vite && vue3 4 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | MG 的编程小屋 14 | 15 | 16 |
17 | 18 | 19 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "src/**/*.{js,vue}": ["eslint --fix", "git add"], 3 | "src/**/*.{vue,html,css,scss,sass}": ["stylelint --fix", "git add"], 4 | "src/**/*.{js,vue,html,css,scss,sass}": [ 5 | "prettier-eslint --write", 6 | "git add", 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ya-vue-blog", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "Micheal Gardner ", 6 | "private": true, 7 | "scripts": { 8 | "start": "vite", 9 | "dev": "vite", 10 | "build": "vite build", 11 | "serve": "vite preview" 12 | }, 13 | "dependencies": { 14 | "@antv/data-set": "^0.11.8", 15 | "@antv/g2": "^4.1.37", 16 | "@antv/g6": "^4.5.2", 17 | "axios": "^0.24.0", 18 | "highlight.js": "^11.4.0", 19 | "markdown-it": "^12.3.2", 20 | "markdown-it-abbr": "^1.0.4", 21 | "markdown-it-deflist": "^2.1.0", 22 | "markdown-it-emoji": "^2.0.0", 23 | "markdown-it-footnote": "^3.0.3", 24 | "markdown-it-ins": "^3.0.1", 25 | "markdown-it-katex": "^2.0.3", 26 | "markdown-it-mark": "^3.0.1", 27 | "markdown-it-sub": "^1.0.0", 28 | "markdown-it-sup": "^1.0.0", 29 | "markdown-it-task-lists": "^2.1.1", 30 | "markdown-it-toc-and-anchor": "^4.2.0", 31 | "moment": "^2.29.1", 32 | "postcss": ">=8.1.0 <9.0.0", 33 | "spectre.css": "^0.5.9", 34 | "typed.js": "^2.0.12", 35 | "vue": "^3.0.0", 36 | "vue-router": "^4.0.0", 37 | "vuex": "^4.0.0" 38 | }, 39 | "devDependencies": { 40 | "@vitejs/plugin-vue": "^2.0.1", 41 | "@vue/compiler-sfc": "^3.2.26", 42 | "autoprefixer": "^10.4.2", 43 | "eslint": "^8.6.0", 44 | "eslint-config-prettier": "^8.3.0", 45 | "eslint-plugin-prettier": "^4.0.0", 46 | "eslint-plugin-vue": "^8.3.0", 47 | "husky": "^7.0.4", 48 | "lint-staged": "^12.1.7", 49 | "postcss-import": "^14.0.2", 50 | "postcss-url": "^10.1.3", 51 | "prettier": "^2.5.1", 52 | "sass": "^1.48.0", 53 | "vite": "^2.7.10" 54 | }, 55 | "browserslist": [ 56 | "> 1%", 57 | "last 2 versions", 58 | "not ie <= 8" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/public/favicon.ico -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 44 | -------------------------------------------------------------------------------- /src/api/apis.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/api/auth.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/api/auth.js -------------------------------------------------------------------------------- /src/api/blog.js: -------------------------------------------------------------------------------- 1 | import {fetchGet, fetchPost} from './http'; 2 | 3 | let base = "" 4 | 5 | export const authRequestLogin = params => { 6 | return fetchPost(`${base}/api/v1/api-token-auth/`, params).then(res => res.data); 7 | }; 8 | 9 | export const authRefreshToken = params => { 10 | return fetchPost(`${base}/api/v1/api-token-refresh/`, params).then(res => res.data) 11 | }; 12 | 13 | /** 14 | * @param title 15 | * @returns {Promise} 16 | */ 17 | export const fetchBlogPost = title => { 18 | return fetchGet(`${base}/api/v1/blog-post/${title}`).then(res => res.data) 19 | }; 20 | 21 | /** 22 | * @param params 23 | * @returns {Promise} 24 | */ 25 | export const fetchPostSummaryList = params => { 26 | return fetchGet(`${base}/api/v1/blog-posts`, params).then(res => res.data) 27 | }; 28 | 29 | /** 30 | * @param params 31 | * @returns {Promise} 32 | */ 33 | export const fetchPostArchive = params => { 34 | return fetchGet(`${base}/api/v1/archive`, params).then(res => res.data) 35 | }; 36 | 37 | /** 38 | * @param params 39 | * @returns {Promise} 40 | */ 41 | export const fetchPostCategroy = params => { 42 | return fetchGet(`${base}/api/v1/category`, params).then(res => res.data) 43 | }; 44 | 45 | /** 46 | * @param params 47 | * @returns {Promise} 48 | */ 49 | export const fetchPostTags = params => { 50 | return fetchGet(`${base}/api/v1/tags`, params).then(res => res.data) 51 | }; 52 | 53 | /** 54 | * @param params 55 | * @returns {Promise} 56 | */ 57 | export const searchBlogPost = params => { 58 | return fetchPost(`${base}/api/v1/blog-posts/search`, params).then(res => res.data) 59 | }; 60 | -------------------------------------------------------------------------------- /src/api/http.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import store from '../store'; 3 | 4 | // import {router} from '../main'; 5 | 6 | export function getAuthorization() { 7 | let token = {Authorization: `JWT ${store.state.access_token}`}; 8 | // console.log(token); 9 | return token 10 | }; 11 | // axios 配置 12 | axios.defaults.timeout = 20000; 13 | 14 | // http request 拦截器 15 | axios.interceptors.request.use( 16 | config => { 17 | if (store.state.access_token) { 18 | config.headers.Authorization = `JWT ${store.state.access_token}`; 19 | } 20 | return config; 21 | }, 22 | err => { 23 | return Promise.reject(err); 24 | }); 25 | 26 | // http response 拦截器 27 | axios.interceptors.response.use( 28 | response => { 29 | return response; 30 | }, 31 | error => { 32 | // console.log(error); 33 | if (error.response) { 34 | switch (error.response.status) { 35 | case 401: 36 | // 401 清除token信息并跳转到登录页面 37 | // router.replace({ 38 | // path: 'login', 39 | // query: {redirect: router.currentRoute.fullPath} 40 | // }) 41 | } 42 | } 43 | // console.log(JSON.stringify(error));//console : Error: Request failed with status code 402 44 | return Promise.reject(error.response.status) 45 | }); 46 | 47 | export default axios; 48 | 49 | /** 50 | * fetch 请求方法 51 | * @param url 52 | * @param params 53 | * @returns {Promise} 54 | */ 55 | export function fetchGet(url, params = {}) { 56 | return new Promise((resolve, reject) => { 57 | axios.get(url, { 58 | params: params 59 | }) 60 | .then(response => { 61 | resolve(response); 62 | }) 63 | .catch(err => { 64 | reject(err) 65 | }) 66 | }) 67 | } 68 | 69 | /** 70 | * post 请求方法 71 | * @param url 72 | * @param data 73 | * @returns {Promise} 74 | */ 75 | export function fetchPost(url, data = {}) { 76 | return new Promise((resolve, reject) => { 77 | axios.post(url, data) 78 | .then(response => { 79 | resolve(response); 80 | }, err => { 81 | reject(err); 82 | }) 83 | }) 84 | } 85 | 86 | /** 87 | * patch 方法封装 88 | * @param url 89 | * @param data 90 | * @returns {Promise} 91 | */ 92 | export function patch(url, data = {}) { 93 | return new Promise((resolve, reject) => { 94 | axios.patch(url, data) 95 | .then(response => { 96 | resolve(response); 97 | }, err => { 98 | reject(err); 99 | }) 100 | }) 101 | } 102 | 103 | /** 104 | * put 方法封装 105 | * @param url 106 | * @param data 107 | * @returns {Promise} 108 | */ 109 | export function put(url, data = {}) { 110 | return new Promise((resolve, reject) => { 111 | axios.put(url, data) 112 | .then(response => { 113 | resolve(response); 114 | }, err => { 115 | reject(err); 116 | }) 117 | }) 118 | } 119 | 120 | /** 121 | * post 请求方法 122 | * @param url 123 | * @param filename 124 | * @param data 125 | * @returns {Promise} 126 | */ 127 | export function downloadPost(url, data = {}, filename) { 128 | return new Promise((resolve, reject) => { 129 | axios({ 130 | method: 'post', 131 | url: url, 132 | responseType: 'arraybuffer', 133 | data: data 134 | }) 135 | .then(function (response) { 136 | let blob = new Blob([response.data], { 137 | type: 'application/x-msdownload' 138 | }); 139 | let link = document.createElement('a') 140 | link.href = window.URL.createObjectURL(blob) 141 | if (filename) { 142 | link.download = filename + `.xlsx` 143 | } else { 144 | link.download = 'report.xlsx' 145 | } 146 | 147 | link.click() 148 | }); 149 | axios.post(url, data) 150 | .then(response => { 151 | resolve(response); 152 | }, err => { 153 | reject(err); 154 | }) 155 | }) 156 | } 157 | -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by twocucao on 2/20/17. 3 | */ 4 | 5 | import * as api from './apis'; 6 | 7 | export default api; 8 | -------------------------------------------------------------------------------- /src/assets/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/assets/images/background.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/styles/_mixins.scss: -------------------------------------------------------------------------------- 1 | @import "mixins/text-truncate"; 2 | -------------------------------------------------------------------------------- /src/assets/styles/_typography.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/assets/styles/_typography.scss -------------------------------------------------------------------------------- /src/assets/styles/_utilities.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/assets/styles/_utilities.scss -------------------------------------------------------------------------------- /src/assets/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | // Core variables 2 | $version: "0.5.0"; 3 | 4 | // Core colors 5 | 6 | // Gray colors 7 | 8 | // Control colors 9 | 10 | // Other colors 11 | 12 | // Fonts 13 | // Credit: https://www.smashingmagazine.com/2015/11/using-system-ui-fonts-practical-guide/ 14 | $base-font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto !default; 15 | $mono-font-family: "SF Mono", "Segoe UI Mono", "Roboto Mono", Menlo, Courier, monospace !default; 16 | $fallback-font-family: "Helvetica Neue", sans-serif !default; 17 | $cjk-zh-font-family: $base-font-family, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", $fallback-font-family !default; 18 | $body-font-family: $base-font-family, $fallback-font-family !default; 19 | 20 | // Unit sizes 21 | 22 | // Font sizes 23 | $html-font-size: 20px !default; 24 | $html-line-height: 1.5 !default; 25 | 26 | // Responsive breakpoints 27 | $size-xs: 480px !default; 28 | $size-sm: 600px !default; 29 | $size-md: 840px !default; 30 | $size-lg: 960px !default; 31 | $size-xl: 1280px !default; 32 | $size-2x: 1440px !default; 33 | 34 | $responsive-breakpoint: $size-xs !default; 35 | 36 | // Z-index 37 | $zindex-0: 1 !default; 38 | $zindex-1: 100 !default; 39 | $zindex-2: 200 !default; 40 | $zindex-3: 300 !default; 41 | $zindex-4: 400 !default; 42 | -------------------------------------------------------------------------------- /src/assets/styles/card.scss: -------------------------------------------------------------------------------- 1 | .ft-intro-card { 2 | margin-top: 20px; 3 | padding: 30px; 4 | width: 100%; 5 | height: 500px; 6 | background-color: #fff; 7 | position: relative; 8 | border-radius: 3px; 9 | transition: box-shadow 0.2s; 10 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 11 | color: #495057; 12 | text-align: left; 13 | .ft-intro-card__title { 14 | margin-top: 20px; 15 | text-align: left; 16 | .ft-intro-card__name { 17 | font-size: 26px; 18 | font-weight: 600; 19 | line-height: 30px; 20 | } 21 | .ft-intro-card__quote { 22 | margin-left: 12px; 23 | font-size: 18px; 24 | white-space: nowrap; 25 | } 26 | } 27 | .ft-intro-card__content { 28 | width: 100%; 29 | font-size: 16px; 30 | line-height: 1.2; 31 | color: #262626; 32 | margin: 18px; 33 | 34 | .ft-intro-card__content-item { 35 | display: flex; 36 | margin-bottom: 18px; 37 | 38 | .ft-intro-card__content-item-label { 39 | width: 100px; 40 | margin-right: 37px; 41 | font-weight: 600; 42 | } 43 | .ft-intro-card__content-item-value { 44 | } 45 | } 46 | } 47 | .ft-intro-card__desc { 48 | font-family: 'Lato', "PingFang SC", "Microsoft YaHei", sans-serif; 49 | text-align: justify; 50 | padding-left: 2.5rem; 51 | padding-top: 1rem; 52 | p { 53 | margin: 0 0 0.5rem; 54 | } 55 | } 56 | } 57 | 58 | .ft-card { 59 | background-color: #fff; 60 | position: relative; 61 | border-radius: 3px; 62 | transition: box-shadow 0.2s; 63 | box-shadow: 1px 1px 2px 0 rgba(0, 0, 0, 0.1); 64 | color: #495057; 65 | text-align: center; 66 | vertical-align: center; 67 | } 68 | 69 | .ft-card-info { 70 | } 71 | 72 | .ft-social-card { 73 | font-size: 20px; 74 | background-color: rgba(174, 238, 21, 0.8); 75 | padding: 20px; 76 | min-height: 90px; 77 | transition: color 0.2s, box-shadow 0.2s; 78 | display: block; 79 | border-radius: 3px; 80 | margin: 15px 7.5px; 81 | text-align: center; 82 | span { 83 | font-size: 40px; 84 | color: #ea6f5a; 85 | } 86 | 87 | p { 88 | font-size: 18px; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/assets/styles/common/transition.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/assets/styles/common/transition.scss -------------------------------------------------------------------------------- /src/assets/styles/common/var.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/assets/styles/common/var.scss -------------------------------------------------------------------------------- /src/assets/styles/global.scss: -------------------------------------------------------------------------------- 1 | // 布局 2 | @import "mixins/cards"; 3 | @import "mixins"; 4 | 5 | .g-wrap { 6 | max-width: 940px; 7 | } 8 | 9 | .g-header { 10 | background-color: rgba(255, 255, 255, .25); 11 | height: 60px; 12 | padding: 10px 60px; 13 | z-index: 2; 14 | box-shadow: 0 0 1px rgba(0, 0, 0, 0.25); 15 | transition: background-color 0.3s ease-in-out; 16 | position: fixed; 17 | width: 100%; 18 | top: 0; 19 | > ul { 20 | display: block; 21 | list-style-type: none; 22 | padding: 0; 23 | position: absolute; 24 | left: 200px; 25 | top: 10px; 26 | height: 40px; 27 | line-height: 40px; 28 | margin: 0 250px 0 0; 29 | li { 30 | display: inline-block; 31 | position: relative; 32 | margin: 0 0.6em; 33 | } 34 | 35 | a { 36 | color: #333; 37 | font-weight: 600; 38 | } 39 | } 40 | 41 | .search-content { 42 | overflow: scroll; 43 | max-height: 600px; 44 | ul, li { 45 | padding: 0; 46 | } 47 | } 48 | } 49 | 50 | .g-sidebar { 51 | @include shadow-card; 52 | margin: 1em 0 0 0; 53 | 54 | //max-width: 250px; 55 | // 这一行比较神器 56 | width: inherit; 57 | position: fixed; 58 | overflow: scroll; 59 | max-height: 800px; 60 | .menu-root { 61 | color: #7f8c8d; 62 | 63 | ul, li { 64 | margin-top: 0; 65 | margin-bottom: 0; 66 | color: #7f8c8d; 67 | a { 68 | color: #7f8c8d; 69 | } 70 | line-height: 1.5em; 71 | } 72 | 73 | h3 { 74 | font-size: 1.2em; 75 | } 76 | } 77 | } 78 | 79 | .card { 80 | em { 81 | background: #37b24d; 82 | color: #fff; 83 | } 84 | } 85 | 86 | .p-article-post { 87 | margin-top: 4.5em; 88 | * { 89 | font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif; 90 | } 91 | .markdownIt-TOC { 92 | line-height: 0.8em; 93 | margin-left: 0; 94 | > li { 95 | font-size: 14px; 96 | margin-top: 1px; 97 | line-height: 1em; 98 | 99 | } 100 | 101 | ul { 102 | padding-top: 0; 103 | padding-bottom: 0; 104 | margin: 0 0 0 0.6rem; 105 | li { 106 | white-space: nowrap; 107 | overflow: hidden; 108 | text-overflow: ellipsis; 109 | } 110 | } 111 | } 112 | } 113 | 114 | .g-article { 115 | @include shadow-card; 116 | padding: 2.2em 50px 2.2em 50px; 117 | color: #555; 118 | 119 | ul, ol { 120 | list-style-type: disc; 121 | margin: 0 0 0 0.6rem; 122 | } 123 | 124 | li { 125 | //list-style-type: circle; 126 | margin: 0 0 0 0.6rem; 127 | } 128 | 129 | h1 { 130 | font-size: 1.8em; 131 | } 132 | 133 | h2 { 134 | font-size: 1.6em; 135 | border-bottom: 1px solid #eee; 136 | padding-bottom: 0.3em; 137 | } 138 | 139 | h3 { 140 | font-size: 1.4em; 141 | } 142 | 143 | h4 { 144 | font-size: 1.3em; 145 | } 146 | 147 | h5 { 148 | font-size: 1.2em; 149 | } 150 | 151 | h6 { 152 | font-size: 1.1em; 153 | } 154 | 155 | h1, h2, h3, h4, h5, h6 { 156 | margin-bottom: .5em; 157 | margin-top: .5em; 158 | } 159 | 160 | p { 161 | margin: 10px 0 10px 0; 162 | } 163 | 164 | blockquote { 165 | p { 166 | margin: 0 0 0 0; 167 | } 168 | } 169 | 170 | figure { 171 | overflow: auto; 172 | } 173 | 174 | pre { 175 | line-height: 1.5em; 176 | margin: 10px 0 10px 0; 177 | font-size: 0.8em; 178 | background-color: #f8f8f8; 179 | code { 180 | font-family: "SF Mono", "Segoe UI Mono", "Roboto Mono", Menlo, Courier, monospace; 181 | } 182 | } 183 | 184 | .posts-expand { 185 | .post-title { 186 | text-align: center; 187 | margin: 0 0 1em; 188 | } 189 | .post-meta { 190 | margin: 3px 0 30px 0; 191 | color: #999; 192 | font-size: 13px; 193 | text-align: center; 194 | .post-wordcount { 195 | display: inline-block; 196 | } 197 | } 198 | } 199 | .post-body { 200 | ul { 201 | display: block; 202 | list-style-type: disc; 203 | } 204 | 205 | li { 206 | list-style: circle; 207 | p { 208 | margin-top: 0; 209 | margin-bottom: 0; 210 | } 211 | } 212 | img { 213 | width: 100%; 214 | } 215 | table { 216 | margin: 20px 0; 217 | width: 100%; 218 | border-collapse: collapse; 219 | border-spacing: 0; 220 | border: 1px solid #ddd; 221 | font-size: 14px; 222 | table-layout: fixed; 223 | td { 224 | border-bottom-width: 1px; 225 | } 226 | 227 | th, td { 228 | border-bottom: 1px solid #ddd; 229 | border-right: 1px solid #eee; 230 | padding: 8px; 231 | text-align: left; 232 | vertical-align: middle; 233 | font-weight: normal; 234 | } 235 | } 236 | 237 | table > tbody > tr:nth-of-type(odd) { 238 | background-color: #f9f9f9; 239 | } 240 | 241 | } 242 | .post-footer { 243 | padding: 20px; 244 | margin: 20px; 245 | box-shadow: 0px 0px 1px 1px rgba(0, 0, 0, .05); 246 | } 247 | } 248 | 249 | .g-content { 250 | } 251 | 252 | .g-holygrail { 253 | display: flex; 254 | min-height: 100vh; 255 | flex-direction: column; 256 | .g-holygrail__body { 257 | display: flex; 258 | flex: 1; 259 | .g-holygrail__content { 260 | flex: 1; 261 | } 262 | @media screen and (min-width: 1280px) { 263 | .g-holygrail__nav, 264 | .g-holygrail__ads { 265 | flex: 0 0 10em; 266 | } 267 | } 268 | 269 | @media screen and (min-width: 960px) and (max-width: 1024px) { 270 | .g-holygrail__nav, 271 | .g-holygrail__ads { 272 | flex: 0 0 8em; 273 | } 274 | } 275 | 276 | @media screen and (min-width: 960px) and (max-width: 1280px) { 277 | .g-holygrail__nav, 278 | .g-holygrail__ads { 279 | flex: 0 0 6em; 280 | } 281 | } 282 | 283 | @media screen and (min-width: 600px) and (max-width: 960px) { 284 | 285 | .g-holygrail__nav, 286 | .g-holygrail__ads { 287 | /* 12em is the width of the columns */ 288 | flex: 0 0 1em; 289 | } 290 | } 291 | 292 | .g-holygrail__nav { 293 | /* put the nav on the left */ 294 | order: -1; 295 | } 296 | } 297 | } 298 | 299 | .g-article-row { 300 | width: 100%; 301 | background: #FFF; 302 | border-radius: 0.4rem; 303 | padding: 0.9rem; 304 | margin: 1rem; 305 | @include shadow-card; 306 | .h3 { 307 | color: #555; 308 | padding-left: 10px; 309 | } 310 | } 311 | 312 | .m-profile-card { 313 | @include shadow-card; 314 | .m-profile-avatar { 315 | display: block; 316 | border-radius: 0.4rem; 317 | width: 100%; 318 | } 319 | 320 | .m-profile-name { 321 | color: #444; 322 | text-align: center; 323 | font-size: 1.2rem; 324 | line-height: 3.5rem; 325 | } 326 | 327 | .m-profile-introduction { 328 | text-align: center; 329 | color: #555; 330 | font-size: 0.9rem; 331 | } 332 | 333 | .m-profile-social { 334 | text-align: center; 335 | line-height: 3.5rem; 336 | .iconfont { 337 | font-size: 1.4rem; 338 | color: #555; 339 | } 340 | 341 | a { 342 | margin: 0 0.3rem; 343 | } 344 | 345 | } 346 | 347 | } 348 | 349 | .m-tags-card, 350 | .m-category-card { 351 | @include shadow-card; 352 | ul { 353 | margin-left: 0; 354 | li { 355 | margin-top: 0.1rem; 356 | } 357 | } 358 | 359 | span { 360 | font-size: 14px; 361 | } 362 | } 363 | 364 | .m-tags-card { 365 | span { 366 | font-size: 12px; 367 | } 368 | } 369 | 370 | .m-simple-card { 371 | @include shadow-card; 372 | margin: 0; 373 | padding-top: 0.5em; 374 | padding-bottom: 0.5em; 375 | } 376 | 377 | .article-suggestions-wrapper { 378 | .article-suggestions-title { 379 | font-weight: 500; 380 | background: #DDDDDD; 381 | } 382 | 383 | .article-suggestions-content { 384 | font-weight: 300; 385 | //@include text-truncate; 386 | } 387 | background: #EEEEEE; 388 | margin-bottom: 10px; 389 | &:hover, 390 | &:focus { 391 | background: #E0E0E0; 392 | cursor: pointer; 393 | } 394 | } 395 | 396 | // 状态 397 | .s-current { 398 | } 399 | 400 | .s-selected { 401 | } 402 | 403 | // 工具 404 | .u-gutter { 405 | margin: 0 3px 0 3px; 406 | } 407 | 408 | .u-gutter-left { 409 | margin-left: 3px; 410 | } 411 | 412 | .u-gutter-right { 413 | margin-right: 3px; 414 | } 415 | 416 | .u-clearfix { 417 | } 418 | 419 | .u-ellipsis { 420 | } 421 | 422 | .u-flex-center { 423 | display: flex; 424 | align-items: center; 425 | justify-content: center; 426 | } 427 | 428 | .u-border-right { 429 | border-right: 1px solid #cac6c6; 430 | } 431 | 432 | // 组件 433 | .m-avatar { 434 | padding: 10px; 435 | img { 436 | border-radius: 50%; 437 | width: 150px; 438 | height: 150px; 439 | } 440 | } 441 | 442 | .m-value { 443 | font-size: 15px; 444 | } 445 | 446 | .m-label { 447 | display: inline-block; 448 | width: 100px; 449 | margin-right: 35px; 450 | font-weight: 600; 451 | font-size: 15px; 452 | } 453 | 454 | .m-slider { 455 | } 456 | 457 | .m-dropMenu { 458 | } 459 | 460 | .m-search-form { 461 | position: absolute; 462 | font-size: 14px; 463 | display: inline-block; 464 | width: 300px; 465 | right: 163px; 466 | input { 467 | //background: #fff none; 468 | border-radius: 4px; 469 | border: 1px solid #dcdfe6; 470 | box-sizing: border-box; 471 | color: #606266; 472 | display: inline-block; 473 | font-size: inherit; 474 | height: 40px; 475 | line-height: 1; 476 | outline: none; 477 | padding: 0 15px; 478 | transition: border-color .2s cubic-bezier(.645, .045, .355, 1); 479 | width: 100%; 480 | &:hover { 481 | outline: none; 482 | border-color: #aeee15; 483 | } 484 | 485 | } 486 | em { 487 | background: #37b24d; 488 | color: #fff; 489 | } 490 | } 491 | 492 | .search-content { 493 | @include shadow-card; 494 | margin: 0; 495 | padding: 0; 496 | } 497 | 498 | .keywords-suggestions, 499 | .article-suggestions { 500 | ul { 501 | display: block; 502 | margin-left: 0; 503 | margin-bottom: 0; 504 | } 505 | li { 506 | display: inline-block; 507 | margin: 1px 5px 1px 0; 508 | width: 100%; 509 | } 510 | 511 | } 512 | 513 | // 钩子 514 | .j-request { 515 | } 516 | 517 | .j-open { 518 | } 519 | 520 | -------------------------------------------------------------------------------- /src/assets/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import "common/transition"; 2 | @import "reset"; 3 | @import "global"; 4 | @import "card"; 5 | @import "pages"; 6 | 7 | -------------------------------------------------------------------------------- /src/assets/styles/mixins/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | // Breakpoint viewport sizes and media queries. 2 | // 3 | // Breakpoints are defined as a map of (name: minimum width), order from small to large: 4 | // 5 | // (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px) 6 | // 7 | // The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default. 8 | 9 | // Name of the next breakpoint, or null for the last breakpoint. 10 | // 11 | // >> breakpoint-next(sm) 12 | // md 13 | // >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) 14 | // md 15 | // >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl)) 16 | // md 17 | @function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) { 18 | $n: index($breakpoint-names, $name); 19 | @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null); 20 | } 21 | 22 | // Minimum breakpoint width. Null for the smallest (first) breakpoint. 23 | // 24 | // >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) 25 | // 576px 26 | @function breakpoint-min($name, $breakpoints: $grid-breakpoints) { 27 | $min: map-get($breakpoints, $name); 28 | @return if($min != 0, $min, null); 29 | } 30 | 31 | // Maximum breakpoint width. Null for the largest (last) breakpoint. 32 | // The maximum value is calculated as the minimum of the next one less 0.02px 33 | // to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths. 34 | // See https://www.w3.org/TR/mediaqueries-4/#mq-min-max 35 | // Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari. 36 | // See https://bugs.webkit.org/show_bug.cgi?id=178261 37 | // 38 | // >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) 39 | // 767.98px 40 | @function breakpoint-max($name, $breakpoints: $grid-breakpoints) { 41 | $next: breakpoint-next($name, $breakpoints); 42 | @return if($next, breakpoint-min($next, $breakpoints) - .02px, null); 43 | } 44 | 45 | // Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront. 46 | // Useful for making responsive utilities. 47 | // 48 | // >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) 49 | // "" (Returns a blank string) 50 | // >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) 51 | // "-sm" 52 | @function breakpoint-infix($name, $breakpoints: $grid-breakpoints) { 53 | @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}"); 54 | } 55 | 56 | // Media of at least the minimum breakpoint width. No query for the smallest breakpoint. 57 | // Makes the @content apply to the given breakpoint and wider. 58 | @mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) { 59 | $min: breakpoint-min($name, $breakpoints); 60 | @if $min { 61 | @media (min-width: $min) { 62 | @content; 63 | } 64 | } @else { 65 | @content; 66 | } 67 | } 68 | 69 | // Media of at most the maximum breakpoint width. No query for the largest breakpoint. 70 | // Makes the @content apply to the given breakpoint and narrower. 71 | @mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) { 72 | $max: breakpoint-max($name, $breakpoints); 73 | @if $max { 74 | @media (max-width: $max) { 75 | @content; 76 | } 77 | } @else { 78 | @content; 79 | } 80 | } 81 | 82 | // Media that spans multiple breakpoint widths. 83 | // Makes the @content apply between the min and max breakpoints 84 | @mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) { 85 | $min: breakpoint-min($lower, $breakpoints); 86 | $max: breakpoint-max($upper, $breakpoints); 87 | 88 | @if $min != null and $max != null { 89 | @media (min-width: $min) and (max-width: $max) { 90 | @content; 91 | } 92 | } @else if $max == null { 93 | @include media-breakpoint-up($lower, $breakpoints) { 94 | @content; 95 | } 96 | } @else if $min == null { 97 | @include media-breakpoint-down($upper, $breakpoints) { 98 | @content; 99 | } 100 | } 101 | } 102 | 103 | // Media between the breakpoint's minimum and maximum widths. 104 | // No minimum for the smallest breakpoint, and no maximum for the largest one. 105 | // Makes the @content apply only to the given breakpoint, not viewports any wider or narrower. 106 | @mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) { 107 | $min: breakpoint-min($name, $breakpoints); 108 | $max: breakpoint-max($name, $breakpoints); 109 | 110 | @if $min != null and $max != null { 111 | @media (min-width: $min) and (max-width: $max) { 112 | @content; 113 | } 114 | } @else if $max == null { 115 | @include media-breakpoint-up($name, $breakpoints) { 116 | @content; 117 | } 118 | } @else if $min == null { 119 | @include media-breakpoint-down($name, $breakpoints) { 120 | @content; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/assets/styles/mixins/_clearfix.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix() { 2 | &::after { 3 | display: block; 4 | clear: both; 5 | content: ""; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/styles/mixins/_float.scss: -------------------------------------------------------------------------------- 1 | @mixin float-left { 2 | float: left !important; 3 | } 4 | 5 | @mixin float-right { 6 | float: right !important; 7 | } 8 | 9 | @mixin float-none { 10 | float: none !important; 11 | } 12 | -------------------------------------------------------------------------------- /src/assets/styles/mixins/_hover.scss: -------------------------------------------------------------------------------- 1 | @mixin hover { 2 | &:hover { 3 | @content; 4 | } 5 | } 6 | 7 | @mixin hover-focus { 8 | &:hover, 9 | &:focus { 10 | @content; 11 | } 12 | } 13 | 14 | @mixin plain-hover-focus { 15 | &, 16 | &:hover, 17 | &:focus { 18 | @content; 19 | } 20 | } 21 | 22 | @mixin hover-focus-active { 23 | &:hover, 24 | &:focus, 25 | &:active { 26 | @content; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/assets/styles/mixins/_lists.scss: -------------------------------------------------------------------------------- 1 | // Lists 2 | 3 | // Unstyled keeps list items block level, just removes default browser padding and list-style 4 | @mixin list-unstyled { 5 | padding-left: 0; 6 | list-style: none; 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/styles/mixins/_text-truncate.scss: -------------------------------------------------------------------------------- 1 | // Text truncate 2 | // Requires inline-block or block for proper styling 3 | 4 | @mixin text-truncate() { 5 | overflow: hidden; 6 | text-overflow: ellipsis; 7 | white-space: nowrap; 8 | } 9 | -------------------------------------------------------------------------------- /src/assets/styles/mixins/cards.scss: -------------------------------------------------------------------------------- 1 | @mixin shadow-card { 2 | border-color: transparent; 3 | color: #333; 4 | background: #FFF; 5 | padding: 1rem; 6 | margin: 1rem; 7 | border-radius: 2px; 8 | box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .05); 9 | } 10 | -------------------------------------------------------------------------------- /src/assets/styles/pages.scss: -------------------------------------------------------------------------------- 1 | .p-home { 2 | margin-top: 4.5em; 3 | } 4 | 5 | .typed-cursor { 6 | opacity: 1; 7 | -webkit-animation: blink 0.7s infinite; 8 | animation: blink 0.7s infinite; 9 | } 10 | 11 | @keyframes blink { 12 | 0% { 13 | opacity: 1; 14 | } 15 | 50% { 16 | opacity: 0; 17 | } 18 | 100% { 19 | opacity: 1; 20 | } 21 | } 22 | 23 | .p-home { 24 | margin-top: 4.5em; 25 | .terminal-window { 26 | text-align: left; 27 | width: 100%; 28 | border-radius: 10px; 29 | position: relative; 30 | height: 400px; 31 | margin: 1rem; 32 | background: url("/static/images/bg_terminal.jpg"); 33 | background-size: cover; 34 | border-color: transparent; 35 | color: #333; 36 | box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .05); 37 | header { 38 | background: #E0E8F0; 39 | height: 30px; 40 | border-radius: 8px 8px 0 0; 41 | padding-left: 10px; 42 | 43 | .btn-close { 44 | height: 12px; 45 | width: 12px; 46 | border-radius: 50%; 47 | position: relative; 48 | top: 5px; 49 | left: 6px; 50 | background-color: #ff3b47; 51 | border: 1px solid #f14231; 52 | display: inline-block; 53 | } 54 | 55 | .btn-max { 56 | height: 12px; 57 | width: 12px; 58 | border-radius: 50%; 59 | position: relative; 60 | top: 5px; 61 | left: 11px; 62 | background-color: #ffc100; 63 | border-color: #9d802c; 64 | display: inline-block; 65 | } 66 | 67 | .btn-min { 68 | height: 12px; 69 | width: 12px; 70 | border-radius: 50%; 71 | position: relative; 72 | top: 5px; 73 | left: 16px; 74 | background-color: #00d742; 75 | border-color: #049931; 76 | display: inline-block; 77 | 78 | } 79 | } 80 | .terminal { 81 | font-family: "SF Mono", "Segoe UI Mono", "Roboto Mono", Menlo, Courier, monospace; 82 | color: white; 83 | font-size: 11pt; 84 | background: rgba(22, 22, 22, 0.7); 85 | min-height: 300px; 86 | padding: 10px; 87 | -webkit-box-sizing: border-box; 88 | box-sizing: border-box; 89 | width: 100%; 90 | overflow: auto; 91 | border-radius: 0 0 8px 8px; 92 | position: absolute; 93 | top: 30px; 94 | bottom: 0; 95 | } 96 | } 97 | } 98 | 99 | .p-blog { 100 | margin-top: 4.5em; 101 | } 102 | 103 | .p-gallery { 104 | margin-top: 4.5em; 105 | } 106 | 107 | .resume-page { 108 | margin-top: 5.5em; 109 | .resume-name { 110 | font-size: 20px; 111 | line-height: 50px; 112 | } 113 | 114 | .resume-avatar { 115 | width: 100px; 116 | height: 100px; 117 | border-radius: 50%; 118 | } 119 | 120 | .datagrid { 121 | overflow: hidden; 122 | border-radius: 3px; 123 | padding: 10px; 124 | margin-left: 8px; 125 | margin-right: 8px; 126 | background-color: rgba(174, 238, 21, 0.8); 127 | table { 128 | text-align: left; 129 | width: 100%; 130 | td, 131 | th { 132 | padding: 7px 10px; 133 | font-size: 15px; 134 | } 135 | tbody { 136 | td { 137 | color: #275420; 138 | font-weight: normal; 139 | } 140 | td:first-child { 141 | border-left: none; 142 | } 143 | tr:last-child td { 144 | border-bottom: none; 145 | } 146 | } 147 | } 148 | } 149 | 150 | .resume-container { 151 | display: flex; 152 | text-align: center; 153 | justify-content: center; 154 | border-color: transparent; 155 | color: #333; 156 | background: #FFF; 157 | padding: 1rem; 158 | border-radius: 2px; 159 | box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .05); 160 | 161 | .navbar-section { 162 | a { 163 | color: #37b24d; 164 | font-size: 20px; 165 | } 166 | } 167 | 168 | .filter-nav { 169 | padding: 10px; 170 | background: #c2cbd4; 171 | border-radius: 3px; 172 | } 173 | 174 | > .columns { 175 | width: 100%; 176 | } 177 | kbd { 178 | font-size: 1rem; 179 | background: #5fc171; 180 | color: #33302f; 181 | } 182 | 183 | &:not(:first-child) { 184 | margin-top: 20px; 185 | padding: 20px; 186 | min-height: 300px; 187 | } 188 | 189 | .timeline { 190 | text-align: left; 191 | } 192 | .resume-container-header { 193 | padding: 0 1em; 194 | text-align: left; 195 | border-left: 0.25em solid #adb5bd; 196 | margin: 15px 0 30px; 197 | } 198 | 199 | .resume-name-wrapper { 200 | display: flex; 201 | justify-content: center; 202 | .resume-name { 203 | align-self: center; 204 | } 205 | } 206 | } 207 | } 208 | 209 | .article-summary-card { 210 | width: 100%; 211 | 212 | .article-summary-inner { 213 | margin: 0.8rem; 214 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .15); 215 | height: 300px; 216 | 217 | &::after { 218 | position: absolute; 219 | top: 0; 220 | left: 0; 221 | width: 100%; 222 | height: 100%; 223 | transition: opacity 2s cubic-bezier(.165, .84, .44, 1); 224 | box-shadow: 0 8px 17px 0 rgba(0, 0, 0, .2), 0 6px 20px 0 rgba(0, 0, 0, .15); 225 | content: ''; 226 | opacity: 0; 227 | z-index: -1; 228 | } 229 | 230 | &:hover, 231 | &:focus { 232 | transform: scale3d(1.006, 1.006, 1); 233 | 234 | &::after { 235 | opacity: 1; 236 | } 237 | } 238 | 239 | .thumbnail { 240 | height: 0; 241 | width: 100%; 242 | display: block; 243 | overflow: hidden; 244 | position: relative; 245 | margin-bottom: 1em; 246 | padding-bottom: 47.11%; 247 | .thumbnail-meta { 248 | background: rgba(240, 240, 250, 0.84); 249 | position: absolute; 250 | bottom: 0px; 251 | right: 0px; 252 | } 253 | .thumbnail-image { 254 | display: block; 255 | position: absolute; 256 | width: 100%; 257 | height: 100%; 258 | opacity: 1; 259 | background-size: cover; 260 | background-position: center; 261 | transition: opacity 0.3s ease-in; 262 | } 263 | } 264 | 265 | .article-title { 266 | h2 { 267 | color: #444; 268 | font-size: 18px; 269 | overflow: hidden; 270 | white-space: nowrap; 271 | text-overflow: ellipsis; 272 | padding: 2px 10px 2px 10px; 273 | } 274 | } 275 | 276 | .article-meta { 277 | display: flex; 278 | padding: 2px 10px 2px 10px; 279 | 280 | .article-category-link, 281 | .article-date { 282 | font-size: 14px; 283 | } 284 | 285 | .article-favor, 286 | .article-reading, 287 | .article-wordcount { 288 | font-size: 11px; 289 | span { 290 | font-size: 11px; 291 | } 292 | } 293 | .article-category { 294 | line-height: 1.3; 295 | } 296 | 297 | div { 298 | color: #555; 299 | } 300 | } 301 | 302 | .article-tags { 303 | overflow: hidden; 304 | white-space: nowrap; 305 | text-overflow: ellipsis; 306 | span { 307 | font-size: 12px; 308 | } 309 | } 310 | 311 | .article-excerpt { 312 | padding: 10px; 313 | color: #555; 314 | } 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /src/assets/styles/reset.scss: -------------------------------------------------------------------------------- 1 | @import 'common/var'; 2 | 3 | *, 4 | *:after, 5 | *:before { 6 | padding: 0; 7 | margin: 0; 8 | box-sizing: border-box; 9 | } 10 | 11 | body, html { 12 | font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; 13 | background-color: #f9f9f9; 14 | color: #333; 15 | height: 100%; 16 | width: 100%; 17 | } 18 | 19 | a { 20 | &:focus, 21 | &:hover, 22 | &:active, 23 | &.active { 24 | text-decoration: none; 25 | box-shadow: 0 0 0 0; 26 | } 27 | } 28 | 29 | ul, ol, li { 30 | list-style-type: none; 31 | margin: 0; 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/styles/theme-future-reboot.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/assets/styles/theme-future-reboot.scss -------------------------------------------------------------------------------- /src/assets/styles/theme-future.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/assets/styles/theme-future.scss -------------------------------------------------------------------------------- /src/assets/styles/utilities/_align.scss: -------------------------------------------------------------------------------- 1 | .align-baseline { 2 | vertical-align: baseline !important; 3 | } 4 | 5 | // Browser default 6 | .align-top { 7 | vertical-align: top !important; 8 | } 9 | 10 | .align-middle { 11 | vertical-align: middle !important; 12 | } 13 | 14 | .align-bottom { 15 | vertical-align: bottom !important; 16 | } 17 | 18 | .align-text-bottom { 19 | vertical-align: text-bottom !important; 20 | } 21 | 22 | .align-text-top { 23 | vertical-align: text-top !important; 24 | } 25 | -------------------------------------------------------------------------------- /src/assets/styles/utilities/_background.scss: -------------------------------------------------------------------------------- 1 | .bg-transparent { 2 | background-color: transparent !important; 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/styles/utilities/_border.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/assets/styles/utilities/_border.scss -------------------------------------------------------------------------------- /src/assets/styles/utilities/_text.scss: -------------------------------------------------------------------------------- 1 | // Alignment 2 | 3 | .text-justify { 4 | text-align: justify !important; 5 | } 6 | 7 | .text-nowrap { 8 | white-space: nowrap !important; 9 | } 10 | 11 | .text-truncate { 12 | @include text-truncate; 13 | } 14 | 15 | // Responsive alignment 16 | 17 | @each $breakpoint in map-keys($grid-breakpoints) { 18 | @include media-breakpoint-up($breakpoint) { 19 | $infix: breakpoint-infix($breakpoint, $grid-breakpoints); 20 | 21 | .text#{$infix}-left { text-align: left !important; } 22 | .text#{$infix}-right { text-align: right !important; } 23 | .text#{$infix}-center { text-align: center !important; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Charts/G2Mixin.ts: -------------------------------------------------------------------------------- 1 | const G2Mixin = { 2 | chartData : {}, 3 | props: { 4 | 'message': { 5 | type: String, 6 | required: true 7 | } 8 | }, 9 | watch: { 10 | handlerRenderChart: function(newVal: Object, oldVal: Object) { 11 | this.renderChart(Object.assign({}, newVal)); 12 | } 13 | }, 14 | created: function () { 15 | this.hello() 16 | }, 17 | 18 | methods: { 19 | hello: function () { 20 | console.log('hello from mixin!') 21 | }, 22 | renderChart: function(newVal: Object) { 23 | console.error('did you implement renderChart'); 24 | } 25 | } 26 | } 27 | 28 | export default G2Mixin; 29 | -------------------------------------------------------------------------------- /src/components/Common/ArticleCard.vue: -------------------------------------------------------------------------------- 1 | 113 | 114 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /src/components/Common/ArticleSummaryCard.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/components/Common/CircleText.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/components/Common/Pagination.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /src/components/Common/VueMarkdown.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/directives/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import hljs from 'highlight.js' 3 | 4 | Vue.directive('highlightjs', { 5 | deep: true, 6 | bind: function (el, binding) { 7 | // on first bind, highlight all targets 8 | let targets = el.querySelectorAll('code') 9 | targets.forEach((target) => { 10 | // if a value is directly assigned to the directive, use this 11 | // instead of the element content. 12 | if (binding.value) { 13 | target.textContent = binding.value 14 | } 15 | hljs.highlightBlock(target) 16 | }) 17 | }, 18 | componentUpdated: function (el, binding) { 19 | // after an update, re-fill the content and then highlight 20 | let targets = el.querySelectorAll('code') 21 | targets.forEach((target) => { 22 | if (binding.value) { 23 | target.textContent = binding.value 24 | hljs.highlightBlock(target) 25 | } 26 | }) 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /src/filters/index.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment' 2 | import Vue from 'vue' 3 | 4 | Vue.filter('formatDate', function (value) { 5 | if (value) { 6 | return moment(String(value)).format('YYYY-MM-DD') 7 | } 8 | }); 9 | 10 | Vue.filter('formatDatetime', function (value) { 11 | if (value) { 12 | return moment(String(value)).format('YYYY-MM-DD hh:mm:ss') 13 | } 14 | }); 15 | 16 | Vue.filter('formatTime', function (value) { 17 | if (value) { 18 | return moment(String(value)).format('hh:mm:ss') 19 | } 20 | }); 21 | 22 | Vue.filter('truncate', function (text, stop, clamp) { 23 | return text.slice(0, stop) + (stop < text.length ? clamp || '...' : '') 24 | }); 25 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | // import Vue from 'vue' 4 | // import store from './store' 5 | import {createApp} from 'vue' 6 | import App from './App.vue' 7 | import {createRouter, createWebHistory} from 'vue-router' 8 | import {routes} from "./routes"; 9 | 10 | 11 | // import './directives' 12 | // import './filters' 13 | import * as VueMarkdown from './components/Common/VueMarkdown.vue'; 14 | 15 | export const router = createRouter({ 16 | history: createWebHistory(), 17 | routes, 18 | }) 19 | const app = createApp(App); 20 | app.use(router) 21 | app.component('vue-markdown', VueMarkdown) 22 | app.mount('#app') 23 | -------------------------------------------------------------------------------- /src/pages/About.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/pages/About/BasicInfo.vue: -------------------------------------------------------------------------------- 1 | 109 | 110 | 121 | -------------------------------------------------------------------------------- /src/pages/About/HobbyInfo.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 24 | -------------------------------------------------------------------------------- /src/pages/About/HobbyProjectInfo.vue: -------------------------------------------------------------------------------- 1 | 92 | 93 | 104 | -------------------------------------------------------------------------------- /src/pages/About/ProjectInfo.vue: -------------------------------------------------------------------------------- 1 | 115 | 116 | 127 | -------------------------------------------------------------------------------- /src/pages/About/SkillInfo.vue: -------------------------------------------------------------------------------- 1 | 152 | 153 | 497 | -------------------------------------------------------------------------------- /src/pages/About/WorkInfo.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | 289 | -------------------------------------------------------------------------------- /src/pages/AboutPageComponents/ProgrammingLanguageUsage.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/pages/AboutPageComponents/TechUsage.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/pages/AboutPageComponents/TestPie.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /src/pages/Archive.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/pages/Blog.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /src/pages/Blog/ArticlePost.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/pages/Gallery.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 74 | -------------------------------------------------------------------------------- /src/pages/Home.vue: -------------------------------------------------------------------------------- 1 | 149 | 150 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /src/pages/Works.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/pages/commons/CategoryCard.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/pages/commons/Footer.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 40 | 41 | 65 | -------------------------------------------------------------------------------- /src/pages/commons/Header.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/pages/commons/ProfileCard.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/pages/commons/PromotionCard.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/pages/commons/TagsCard.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | export const routes = [ 2 | { 3 | path: '/', 4 | name: 'home', 5 | component: () => import('./pages/Home.vue') 6 | }, 7 | // 解决手贱带来的问题 8 | { 9 | path: '/index:suffix*', 10 | name: 'index', 11 | component: () => import('./pages/Home.vue') 12 | }, 13 | { 14 | path: '/blog', 15 | name: 'blog', 16 | component: () => import('./pages/Blog.vue') 17 | }, 18 | { 19 | path: '/blog/post/:title', 20 | name: 'post', 21 | component: () => import('./pages/Blog/ArticlePost.vue') 22 | }, 23 | { 24 | path: '/blog/:category(category/\\d+)?/:tags(tags/\\d+)?/:page(page/\\d+)?', 25 | name: 'blogposts', 26 | component: () => import('./pages/Blog.vue') 27 | }, 28 | { 29 | path: '/archive', 30 | name: 'archive', 31 | component: () => import('./pages/Archive.vue') 32 | }, 33 | { 34 | path: '/gallery', 35 | name: 'gallery', 36 | component: () => import('./pages/Gallery.vue') 37 | }, 38 | { 39 | path: '/works', 40 | name: 'works', 41 | component: () => import('./pages/Works.vue') 42 | }, 43 | { 44 | path: '/about', 45 | name: 'about', 46 | component: () => import('./pages/About.vue') 47 | } 48 | ]; 49 | -------------------------------------------------------------------------------- /src/store/actions.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/store/actions.js -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/store/getters.js -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import * as getters from './getters' 2 | import * as actions from './actions' 3 | import * as mutations from './mutations' 4 | 5 | import {createStore} from 'vuex' 6 | 7 | 8 | const state = { 9 | count: 0, 10 | user_info: null, 11 | access_token: null, 12 | history: [] 13 | } 14 | 15 | const store = createStore({ 16 | state, 17 | getters, 18 | actions, 19 | mutations 20 | }) 21 | 22 | export default store; 23 | -------------------------------------------------------------------------------- /src/store/mutations.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/src/store/mutations.js -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/static/.gitkeep -------------------------------------------------------------------------------- /static/hightlight/solarized-dark.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #002b36; 12 | color: #839496; 13 | } 14 | 15 | .hljs-comment, 16 | .hljs-quote { 17 | color: #586e75; 18 | } 19 | 20 | /* Solarized Green */ 21 | .hljs-keyword, 22 | .hljs-selector-tag, 23 | .hljs-addition { 24 | color: #859900; 25 | } 26 | 27 | /* Solarized Cyan */ 28 | .hljs-number, 29 | .hljs-string, 30 | .hljs-meta .hljs-meta-string, 31 | .hljs-literal, 32 | .hljs-doctag, 33 | .hljs-regexp { 34 | color: #2aa198; 35 | } 36 | 37 | /* Solarized Blue */ 38 | .hljs-title, 39 | .hljs-section, 40 | .hljs-name, 41 | .hljs-selector-id, 42 | .hljs-selector-class { 43 | color: #268bd2; 44 | } 45 | 46 | /* Solarized Yellow */ 47 | .hljs-attribute, 48 | .hljs-attr, 49 | .hljs-variable, 50 | .hljs-template-variable, 51 | .hljs-class .hljs-title, 52 | .hljs-type { 53 | color: #b58900; 54 | } 55 | 56 | /* Solarized Orange */ 57 | .hljs-symbol, 58 | .hljs-bullet, 59 | .hljs-subst, 60 | .hljs-meta, 61 | .hljs-meta .hljs-keyword, 62 | .hljs-selector-attr, 63 | .hljs-selector-pseudo, 64 | .hljs-link { 65 | color: #cb4b16; 66 | } 67 | 68 | /* Solarized Red */ 69 | .hljs-built_in, 70 | .hljs-deletion { 71 | color: #dc322f; 72 | } 73 | 74 | .hljs-formula { 75 | background: #073642; 76 | } 77 | 78 | .hljs-emphasis { 79 | font-style: italic; 80 | } 81 | 82 | .hljs-strong { 83 | font-weight: bold; 84 | } 85 | -------------------------------------------------------------------------------- /static/hightlight/solarized-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #fdf6e3; 12 | color: #657b83; 13 | } 14 | 15 | .hljs-comment, 16 | .hljs-quote { 17 | color: #93a1a1; 18 | } 19 | 20 | /* Solarized Green */ 21 | .hljs-keyword, 22 | .hljs-selector-tag, 23 | .hljs-addition { 24 | color: #859900; 25 | } 26 | 27 | /* Solarized Cyan */ 28 | .hljs-number, 29 | .hljs-string, 30 | .hljs-meta .hljs-meta-string, 31 | .hljs-literal, 32 | .hljs-doctag, 33 | .hljs-regexp { 34 | color: #2aa198; 35 | } 36 | 37 | /* Solarized Blue */ 38 | .hljs-title, 39 | .hljs-section, 40 | .hljs-name, 41 | .hljs-selector-id, 42 | .hljs-selector-class { 43 | color: #268bd2; 44 | } 45 | 46 | /* Solarized Yellow */ 47 | .hljs-attribute, 48 | .hljs-attr, 49 | .hljs-variable, 50 | .hljs-template-variable, 51 | .hljs-class .hljs-title, 52 | .hljs-type { 53 | color: #b58900; 54 | } 55 | 56 | /* Solarized Orange */ 57 | .hljs-symbol, 58 | .hljs-bullet, 59 | .hljs-subst, 60 | .hljs-meta, 61 | .hljs-meta .hljs-keyword, 62 | .hljs-selector-attr, 63 | .hljs-selector-pseudo, 64 | .hljs-link { 65 | color: #cb4b16; 66 | } 67 | 68 | /* Solarized Red */ 69 | .hljs-built_in, 70 | .hljs-deletion { 71 | color: #dc322f; 72 | } 73 | 74 | .hljs-formula { 75 | background: #eee8d5; 76 | } 77 | 78 | .hljs-emphasis { 79 | font-style: italic; 80 | } 81 | 82 | .hljs-strong { 83 | font-weight: bold; 84 | } 85 | -------------------------------------------------------------------------------- /static/iconfont/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/static/iconfont/.gitkeep -------------------------------------------------------------------------------- /static/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face { 3 | font-family: 'iconfont'; 4 | src: url('./iconfont.eot'); 5 | src: url('./iconfont.eot?#iefix') format('embedded-opentype'), 6 | url('./iconfont.woff') format('woff'), 7 | url('./iconfont.ttf') format('truetype'), 8 | url('./iconfont.svg#iconfont') format('svg'); 9 | } 10 | 11 | .iconfont { 12 | font-family:"iconfont" !important; 13 | font-size:16px; 14 | font-style:normal; 15 | font-weight: normal; 16 | -webkit-font-smoothing: antialiased; 17 | -moz-osx-font-smoothing: grayscale; 18 | } 19 | 20 | [class^="iconfont-"], [class*=" iconfont-"] { 21 | /* use !important to prevent issues with browser extensions that change fonts */ 22 | font-family: 'iconfont' !important; 23 | speak: none; 24 | font-style: normal; 25 | font-weight: normal; 26 | font-variant: normal; 27 | text-transform: none; 28 | line-height: 1; 29 | 30 | /* Better Font Rendering =========== */ 31 | -webkit-font-smoothing: antialiased; 32 | -moz-osx-font-smoothing: grayscale; 33 | } 34 | 35 | 36 | .iconfont-github:before { 37 | content: "\E914"; 38 | } 39 | 40 | 41 | .iconfont-heart:before { 42 | content: "\E915"; 43 | } 44 | 45 | 46 | .iconfont-jianshu:before { 47 | content: "\E916"; 48 | } 49 | 50 | 51 | .iconfont-juejin:before { 52 | content: "\E917"; 53 | } 54 | 55 | 56 | .iconfont-python:before { 57 | content: "\E918"; 58 | } 59 | 60 | 61 | .iconfont-reading:before { 62 | content: "\E919"; 63 | } 64 | 65 | 66 | .iconfont-wordcount:before { 67 | content: "\E91A"; 68 | } 69 | 70 | 71 | .iconfont-zhihu:before { 72 | content: "\E91B"; 73 | } 74 | -------------------------------------------------------------------------------- /static/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/static/iconfont/iconfont.eot -------------------------------------------------------------------------------- /static/iconfont/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Created by font-carrier 6 | 7 | 8 | 9 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /static/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/static/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /static/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/static/iconfont/iconfont.woff -------------------------------------------------------------------------------- /static/images/bg_terminal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/static/images/bg_terminal.jpg -------------------------------------------------------------------------------- /static/images/mp_wechat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/static/images/mp_wechat.jpg -------------------------------------------------------------------------------- /static/images/shoes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/static/images/shoes.jpg -------------------------------------------------------------------------------- /static/images/tips_wechat.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hylarucoder/YaVueBlog/99c05b75f71d0dc5219f2feea7e64e9349206db3/static/images/tips_wechat.jpeg -------------------------------------------------------------------------------- /static/js/smooth-scroll.min.js: -------------------------------------------------------------------------------- 1 | /*! smooth-scroll v12.1.5 | (c) 2017 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/smooth-scroll */ 2 | !(function(e,t){"function"==typeof define&&define.amd?define([],(function(){return t(e)})):"object"==typeof exports?module.exports=t(e):e.SmoothScroll=t(e)})("undefined"!=typeof global?global:"undefined"!=typeof window?window:this,(function(e){"use strict";var t="querySelector"in document&&"addEventListener"in e&&"requestAnimationFrame"in e&&"closest"in e.Element.prototype,n={ignore:"[data-scroll-ignore]",header:null,speed:500,offset:0,easing:"easeInOutCubic",customEasing:null,before:function(){},after:function(){}},o=function(){for(var e={},t=0,n=arguments.length;t=1&&t<=31||127==t||0===a&&t>=48&&t<=57||1===a&&t>=48&&t<=57&&45===i?r+="\\"+t.toString(16)+" ":r+=t>=128||45===t||95===t||t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122?n.charAt(a):"\\"+n.charAt(a)}return"#"+r},i=function(e,t){var n;return"easeInQuad"===e.easing&&(n=t*t),"easeOutQuad"===e.easing&&(n=t*(2-t)),"easeInOutQuad"===e.easing&&(n=t<.5?2*t*t:(4-2*t)*t-1),"easeInCubic"===e.easing&&(n=t*t*t),"easeOutCubic"===e.easing&&(n=--t*t*t+1),"easeInOutCubic"===e.easing&&(n=t<.5?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1),"easeInQuart"===e.easing&&(n=t*t*t*t),"easeOutQuart"===e.easing&&(n=1- --t*t*t*t),"easeInOutQuart"===e.easing&&(n=t<.5?8*t*t*t*t:1-8*--t*t*t*t),"easeInQuint"===e.easing&&(n=t*t*t*t*t),"easeOutQuint"===e.easing&&(n=1+--t*t*t*t*t),"easeInOutQuint"===e.easing&&(n=t<.5?16*t*t*t*t*t:1+16*--t*t*t*t*t),e.customEasing&&(n=e.customEasing(t)),n||t},u=function(){return Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},c=function(e,t,n){var o=0;if(e.offsetParent)do{o+=e.offsetTop,e=e.offsetParent}while(e);return o=Math.max(o-t-n,0)},s=function(e){return e?a(e)+e.offsetTop:0},l=function(t,n,o){o||(t.focus(),document.activeElement.id!==t.id&&(t.setAttribute("tabindex","-1"),t.focus(),t.style.outline="none"),e.scrollTo(0,n))},f=function(t){return!!("matchMedia"in e&&e.matchMedia("(prefers-reduced-motion)").matches)};return function(a,d){var m,h,g,p,v,b,y,S={};S.cancelScroll=function(){cancelAnimationFrame(y)},S.animateScroll=function(t,a,r){var f=o(m||n,r||{}),d="[object Number]"===Object.prototype.toString.call(t),h=d||!t.tagName?null:t;if(d||h){var g=e.pageYOffset;f.header&&!p&&(p=document.querySelector(f.header)),v||(v=s(p));var b,y,E,I=d?t:c(h,v,parseInt("function"==typeof f.offset?f.offset():f.offset,10)),O=I-g,A=u(),C=0,w=function(n,o){var r=e.pageYOffset;if(n==o||r==o||(g=A)return S.cancelScroll(),l(t,o,d),f.after(t,a),b=null,!0},Q=function(t){b||(b=t),C+=t-b,y=C/parseInt(f.speed,10),y=y>1?1:y,E=g+O*i(f,y),e.scrollTo(0,Math.floor(E)),w(E,I)||(e.requestAnimationFrame(Q),b=t)};0===e.pageYOffset&&e.scrollTo(0,0),f.before(t,a),S.cancelScroll(),e.requestAnimationFrame(Q)}};var E=function(e){h&&(h.id=h.getAttribute("data-scroll-id"),S.animateScroll(h,g),h=null,g=null)},I=function(t){if(!f()&&0===t.button&&!t.metaKey&&!t.ctrlKey&&(g=t.target.closest(a))&&"a"===g.tagName.toLowerCase()&&!t.target.closest(m.ignore)&&g.hostname===e.location.hostname&&g.pathname===e.location.pathname&&/#/.test(g.href)){var n;try{n=r(decodeURIComponent(g.hash))}catch(e){n=r(g.hash)}if("#"===n){t.preventDefault(),h=document.body;var o=h.id?h.id:"smooth-scroll-top";return h.setAttribute("data-scroll-id",o),h.id="",void(e.location.hash.substring(1)===o?E():e.location.hash=o)}h=document.querySelector(n),h&&(h.setAttribute("data-scroll-id",h.id),h.id="",g.hash===e.location.hash&&(t.preventDefault(),E()))}},O=function(e){b||(b=setTimeout((function(){b=null,v=s(p)}),66))};return S.destroy=function(){m&&(document.removeEventListener("click",I,!1),e.removeEventListener("resize",O,!1),S.cancelScroll(),m=null,h=null,g=null,p=null,v=null,b=null,y=null)},S.init=function(a){t&&(S.destroy(),m=o(n,a||{}),p=m.header?document.querySelector(m.header):null,v=s(p),document.addEventListener("click",I,!1),e.addEventListener("hashchange",E,!1),p&&e.addEventListener("resize",O,!1))},S.init(d),S}})); -------------------------------------------------------------------------------- /test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // The assertion name is the filename. 3 | // Example usage: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // For more information on custom assertions see: 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | 10 | exports.assertion = function (selector, count) { 11 | this.message = 'Testing if element <' + selector + '> has count: ' + count 12 | this.expected = count 13 | this.pass = function (val) { 14 | return val === this.expected 15 | } 16 | this.value = function (res) { 17 | return res.value 18 | } 19 | this.command = function (cb) { 20 | var self = this 21 | return this.api.execute(function (selector) { 22 | return document.querySelectorAll(selector).length 23 | }, [selector], function (res) { 24 | cb.call(self, res) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | var config = require('../../config') 3 | 4 | // http://nightwatchjs.org/gettingstarted#settings-file 5 | module.exports = { 6 | src_folders: ['test/e2e/specs'], 7 | output_folder: 'test/e2e/reports', 8 | custom_assertions_path: ['test/e2e/custom-assertions'], 9 | 10 | selenium: { 11 | start_process: true, 12 | server_path: require('selenium-server').path, 13 | host: '127.0.0.1', 14 | port: 4444, 15 | cli_args: { 16 | 'webdriver.chrome.driver': require('chromedriver').path 17 | } 18 | }, 19 | 20 | test_settings: { 21 | default: { 22 | selenium_port: 4444, 23 | selenium_host: 'localhost', 24 | silent: true, 25 | globals: { 26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) 27 | } 28 | }, 29 | 30 | chrome: { 31 | desiredCapabilities: { 32 | browserName: 'chrome', 33 | javascriptEnabled: true, 34 | acceptSslCerts: true 35 | } 36 | }, 37 | 38 | firefox: { 39 | desiredCapabilities: { 40 | browserName: 'firefox', 41 | javascriptEnabled: true, 42 | acceptSslCerts: true 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | 4 | const webpack = require('webpack') 5 | const DevServer = require('webpack-dev-server') 6 | 7 | const webpackConfig = require('../../build/webpack.prod.conf') 8 | const devConfigPromise = require('../../build/webpack.dev.conf') 9 | 10 | let server 11 | 12 | devConfigPromise.then(devConfig => { 13 | const devServerOptions = devConfig.devServer 14 | const compiler = webpack(webpackConfig) 15 | server = new DevServer(compiler, devServerOptions) 16 | const port = devServerOptions.port 17 | const host = devServerOptions.host 18 | return server.listen(port, host) 19 | }) 20 | .then(() => { 21 | // 2. run the nightwatch test suite against it 22 | // to run in additional browsers: 23 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings" 24 | // 2. add it to the --env flag below 25 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 26 | // For more information on Nightwatch's config file, see 27 | // http://nightwatchjs.org/guide#settings-file 28 | let opts = process.argv.slice(2) 29 | if (opts.indexOf('--config') === -1) { 30 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 31 | } 32 | if (opts.indexOf('--env') === -1) { 33 | opts = opts.concat(['--env', 'chrome']) 34 | } 35 | 36 | const spawn = require('cross-spawn') 37 | const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 38 | 39 | runner.on('exit', function (code) { 40 | server.close() 41 | process.exit(code) 42 | }) 43 | 44 | runner.on('error', function (err) { 45 | server.close() 46 | throw err 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | // automatically uses dev Server port from /config.index.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL 10 | 11 | browser 12 | .url(devServer) 13 | .waitForElementVisible('#app', 5000) 14 | .assert.elementPresent('.hello') 15 | .assert.containsText('h1', 'Welcome to Your Vue.js App') 16 | .assert.elementCount('img', 1) 17 | .end() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | }, 5 | "globals": { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/unit/jest.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | rootDir: path.resolve(__dirname, '../../'), 5 | moduleFileExtensions: [ 6 | 'js', 7 | 'json', 8 | 'vue' 9 | ], 10 | moduleNameMapper: { 11 | '^@/(.*)$': '/src/$1' 12 | }, 13 | transform: { 14 | '^.+\\.js$': '/node_modules/babel-jest', 15 | '.*\\.(vue)$': '/node_modules/vue-jest' 16 | }, 17 | testPathIgnorePatterns: [ 18 | '/test/e2e' 19 | ], 20 | snapshotSerializers: ['/node_modules/jest-serializer-vue'], 21 | setupFiles: ['/test/unit/setup'], 22 | mapCoverage: true, 23 | coverageDirectory: '/test/unit/coverage', 24 | collectCoverageFrom: [ 25 | 'src/**/*.{js,vue}', 26 | '!src/main.js', 27 | '!src/routes/index.js', 28 | '!**/node_modules/**' 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /test/unit/setup.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.config.productionTip = false 4 | -------------------------------------------------------------------------------- /test/unit/specs/HelloWorld.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import HelloWorld from '@/components/HelloWorld' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('should render correct contents', () => { 6 | const Constructor = Vue.extend(HelloWorld) 7 | const vm = new Constructor().$mount() 8 | expect(vm.$el.querySelector('.hello h1').textContent) 9 | .toEqual('Welcome to Your Vue.js App') 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()] 7 | }) 8 | --------------------------------------------------------------------------------