├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── README.md ├── cooking.conf.js ├── dist ├── favicon.ico ├── index.html └── login.html ├── favicon.ico ├── package.json ├── postcss.config.js ├── src ├── App.jsx ├── LoginApp.jsx ├── api │ └── index.js ├── assets │ ├── css │ │ ├── hljs │ │ │ ├── github.css │ │ │ ├── googlecode.css │ │ │ └── hljs.css │ │ ├── login.css │ │ ├── simditor.css │ │ └── style.css │ └── images │ │ ├── arrow.jpg │ │ ├── icon.png │ │ ├── logo.png │ │ ├── w-icon-1.png │ │ ├── w-icon-2.png │ │ ├── w-icon-3.png │ │ └── w-icon.png ├── components │ ├── about.jsx │ ├── app │ │ └── ajax-form.jsx │ ├── comment.jsx │ └── index-post.jsx ├── config.js ├── login.js ├── main.js ├── pages │ ├── admin-edit.jsx │ ├── admin-list.jsx │ ├── admin-post.jsx │ ├── article.jsx │ └── index.jsx ├── polyfill │ └── index.js ├── router │ └── index.js ├── store │ ├── actions.js │ ├── getters.js │ ├── index.js │ ├── modules │ │ ├── admin.js │ │ ├── global.js │ │ └── topics.js │ └── mutation-types.js ├── template │ ├── index.html │ └── login.html └── utils │ └── index.js ├── static ├── editor.md │ ├── css │ │ ├── editormd.css │ │ ├── editormd.logo.css │ │ ├── editormd.logo.min.css │ │ ├── editormd.min.css │ │ ├── editormd.preview.css │ │ └── editormd.preview.min.css │ ├── editormd.amd.js │ ├── editormd.amd.min.js │ ├── editormd.js │ ├── editormd.min.js │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── editormd-logo.eot │ │ ├── editormd-logo.svg │ │ ├── editormd-logo.ttf │ │ ├── editormd-logo.woff │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ ├── images │ │ ├── loading.gif │ │ ├── loading@2x.gif │ │ ├── loading@3x.gif │ │ └── logos │ │ │ ├── editormd-favicon-16x16.ico │ │ │ ├── editormd-favicon-24x24.ico │ │ │ ├── editormd-favicon-32x32.ico │ │ │ ├── editormd-favicon-48x48.ico │ │ │ ├── editormd-favicon-64x64.ico │ │ │ ├── editormd-logo-114x114.png │ │ │ ├── editormd-logo-120x120.png │ │ │ ├── editormd-logo-144x144.png │ │ │ ├── editormd-logo-16x16.png │ │ │ ├── editormd-logo-180x180.png │ │ │ ├── editormd-logo-240x240.png │ │ │ ├── editormd-logo-24x24.png │ │ │ ├── editormd-logo-320x320.png │ │ │ ├── editormd-logo-32x32.png │ │ │ ├── editormd-logo-48x48.png │ │ │ ├── editormd-logo-57x57.png │ │ │ ├── editormd-logo-64x64.png │ │ │ ├── editormd-logo-72x72.png │ │ │ ├── editormd-logo-96x96.png │ │ │ └── vi.png │ ├── languages │ │ ├── en.js │ │ └── zh-tw.js │ ├── lib │ │ ├── codemirror │ │ │ ├── AUTHORS │ │ │ ├── addon │ │ │ │ ├── comment │ │ │ │ │ ├── comment.js │ │ │ │ │ └── continuecomment.js │ │ │ │ ├── dialog │ │ │ │ │ ├── dialog.css │ │ │ │ │ └── dialog.js │ │ │ │ ├── display │ │ │ │ │ ├── fullscreen.css │ │ │ │ │ ├── fullscreen.js │ │ │ │ │ ├── panel.js │ │ │ │ │ ├── placeholder.js │ │ │ │ │ └── rulers.js │ │ │ │ ├── edit │ │ │ │ │ ├── closebrackets.js │ │ │ │ │ ├── closetag.js │ │ │ │ │ ├── continuelist.js │ │ │ │ │ ├── matchbrackets.js │ │ │ │ │ ├── matchtags.js │ │ │ │ │ └── trailingspace.js │ │ │ │ ├── fold │ │ │ │ │ ├── brace-fold.js │ │ │ │ │ ├── comment-fold.js │ │ │ │ │ ├── foldcode.js │ │ │ │ │ ├── foldgutter.css │ │ │ │ │ ├── foldgutter.js │ │ │ │ │ ├── indent-fold.js │ │ │ │ │ ├── markdown-fold.js │ │ │ │ │ └── xml-fold.js │ │ │ │ ├── hint │ │ │ │ │ ├── anyword-hint.js │ │ │ │ │ ├── css-hint.js │ │ │ │ │ ├── html-hint.js │ │ │ │ │ ├── javascript-hint.js │ │ │ │ │ ├── show-hint.css │ │ │ │ │ ├── show-hint.js │ │ │ │ │ ├── sql-hint.js │ │ │ │ │ └── xml-hint.js │ │ │ │ ├── lint │ │ │ │ │ ├── coffeescript-lint.js │ │ │ │ │ ├── css-lint.js │ │ │ │ │ ├── javascript-lint.js │ │ │ │ │ ├── json-lint.js │ │ │ │ │ ├── lint.css │ │ │ │ │ ├── lint.js │ │ │ │ │ └── yaml-lint.js │ │ │ │ ├── merge │ │ │ │ │ ├── merge.css │ │ │ │ │ └── merge.js │ │ │ │ ├── mode │ │ │ │ │ ├── loadmode.js │ │ │ │ │ ├── multiplex.js │ │ │ │ │ ├── multiplex_test.js │ │ │ │ │ ├── overlay.js │ │ │ │ │ └── simple.js │ │ │ │ ├── runmode │ │ │ │ │ ├── colorize.js │ │ │ │ │ ├── runmode-standalone.js │ │ │ │ │ ├── runmode.js │ │ │ │ │ └── runmode.node.js │ │ │ │ ├── scroll │ │ │ │ │ ├── annotatescrollbar.js │ │ │ │ │ ├── scrollpastend.js │ │ │ │ │ ├── simplescrollbars.css │ │ │ │ │ └── simplescrollbars.js │ │ │ │ ├── search │ │ │ │ │ ├── match-highlighter.js │ │ │ │ │ ├── matchesonscrollbar.css │ │ │ │ │ ├── matchesonscrollbar.js │ │ │ │ │ ├── search.js │ │ │ │ │ └── searchcursor.js │ │ │ │ ├── selection │ │ │ │ │ ├── active-line.js │ │ │ │ │ ├── mark-selection.js │ │ │ │ │ └── selection-pointer.js │ │ │ │ ├── tern │ │ │ │ │ ├── tern.css │ │ │ │ │ ├── tern.js │ │ │ │ │ └── worker.js │ │ │ │ └── wrap │ │ │ │ │ └── hardwrap.js │ │ │ ├── addons.min.js │ │ │ ├── codemirror.min.css │ │ │ ├── codemirror.min.js │ │ │ ├── lib │ │ │ │ ├── codemirror.css │ │ │ │ └── codemirror.js │ │ │ ├── modes.min.js │ │ │ └── theme │ │ │ │ ├── 3024-day.css │ │ │ │ ├── 3024-night.css │ │ │ │ ├── ambiance-mobile.css │ │ │ │ ├── ambiance.css │ │ │ │ ├── base16-dark.css │ │ │ │ ├── base16-light.css │ │ │ │ ├── blackboard.css │ │ │ │ ├── cobalt.css │ │ │ │ ├── colorforth.css │ │ │ │ ├── eclipse.css │ │ │ │ ├── elegant.css │ │ │ │ ├── erlang-dark.css │ │ │ │ ├── lesser-dark.css │ │ │ │ ├── mbo.css │ │ │ │ ├── mdn-like.css │ │ │ │ ├── midnight.css │ │ │ │ ├── monokai.css │ │ │ │ ├── neat.css │ │ │ │ ├── neo.css │ │ │ │ ├── night.css │ │ │ │ ├── paraiso-dark.css │ │ │ │ ├── paraiso-light.css │ │ │ │ ├── pastel-on-dark.css │ │ │ │ ├── rubyblue.css │ │ │ │ ├── solarized.css │ │ │ │ ├── the-matrix.css │ │ │ │ ├── tomorrow-night-bright.css │ │ │ │ ├── tomorrow-night-eighties.css │ │ │ │ ├── twilight.css │ │ │ │ ├── vibrant-ink.css │ │ │ │ ├── xq-dark.css │ │ │ │ ├── xq-light.css │ │ │ │ └── zenburn.css │ │ ├── flowchart.min.js │ │ ├── jquery.flowchart.min.js │ │ ├── marked.min.js │ │ ├── prettify.min.js │ │ ├── raphael.min.js │ │ ├── sequence-diagram.min.js │ │ └── underscore.min.js │ ├── plugins │ │ ├── code-block-dialog │ │ │ └── code-block-dialog.js │ │ ├── emoji-dialog │ │ │ ├── emoji-dialog.js │ │ │ └── emoji.json │ │ ├── goto-line-dialog │ │ │ └── goto-line-dialog.js │ │ ├── help-dialog │ │ │ ├── help-dialog.js │ │ │ └── help.md │ │ ├── html-entities-dialog │ │ │ ├── html-entities-dialog.js │ │ │ └── html-entities.json │ │ ├── image-dialog │ │ │ └── image-dialog.js │ │ ├── link-dialog │ │ │ └── link-dialog.js │ │ ├── plugin-template.js │ │ ├── preformatted-text-dialog │ │ │ └── preformatted-text-dialog.js │ │ ├── reference-link-dialog │ │ │ └── reference-link-dialog.js │ │ ├── table-dialog │ │ │ └── table-dialog.js │ │ └── test-plugin │ │ │ └── test-plugin.js │ └── src │ │ └── editormd.js └── favicon.ico └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": [ 4 | "transform-vue-jsx", 5 | ["transform-runtime", { 6 | "polyfill": true, 7 | "regenerator": true 8 | }] 9 | ], 10 | "comments": false 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "lcy-react", 3 | "rules": { 4 | "react/jsx-handler-names": 0, 5 | "eact/self-closing-comp": 0 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log 4 | .vscode/ 5 | proxy.js 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mmf-blog vuejs 2.0 jsx语法版 2 | 3 | demo: [http://www.mmxiaowu.com](http://www.mmxiaowu.com) 4 | 5 | --- 6 | 7 | #### 其他版本 8 | 9 | react版本: [https://github.com/lincenying/mmf-blog-react](https://github.com/lincenying/mmf-blog-react) 10 | 11 | react(dva)版本: [https://github.com/lincenying/mmf-blog-dva](https://github.com/lincenying/mmf-blog-dva) 12 | 13 | vue1版本: [https://github.com/lincenying/mmf-blog-vue](https://github.com/lincenying/mmf-blog-vue) 14 | 15 | vue2版本: [https://github.com/lincenying/mmf-blog-vue2](https://github.com/lincenying/mmf-blog-vue2) 16 | 17 | vue2(jsx语法)版本: [https://github.com/lincenying/mmf-blog-vue2-jsx](https://github.com/lincenying/mmf-blog-vue2-jsx) 18 | 19 | vue2(jsx语法,leancloud)版本: [https://github.com/lincenying/mmf-blog-vue2-jsx-lc](https://github.com/lincenying/mmf-blog-vue2-jsx-lc) 20 | 21 | vue2 服务端渲染版本: [https://github.com/lincenying/mmf-blog-vue2-ssr](https://github.com/lincenying/mmf-blog-vue2-ssr) 22 | 23 | --- 24 | 25 | 先安装 api server: https://github.com/lincenying/mmf-blog-api 26 | 27 | ``` 28 | // 安装cooking 29 | npm install cooking-cli -g 30 | 31 | // 安装依赖 32 | npm install or yarn 33 | 34 | // 生产环境 35 | npm run build 36 | 37 | // 开发环境 38 | npm run dev 39 | ``` 40 | 41 | 首页 42 | http://localhost:8080 43 | 44 | 登录 45 | http://localhost:8080/login.html 46 | -------------------------------------------------------------------------------- /cooking.conf.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | var path = require('path') 4 | var cooking = require('cooking') 5 | var webpack = require('webpack') 6 | var CopyWebpackPlugin = require('copy-webpack-plugin') 7 | 8 | var config = { 9 | entry: { 10 | app: './src/main.js', 11 | login: './src/login.js', 12 | vendor: ['vue', 'vue-router', 'vuex', 'vuex-router-sync', 'store2', 'toastr', './src/polyfill'] 13 | }, 14 | dist: './dist/static', 15 | externals: { 16 | 'jquery': 'jQuery' 17 | }, 18 | devServer: { 19 | port: 8080, 20 | publicPath: '/', 21 | proxy: { 22 | '/api/**': { 23 | target: 'http://localhost:3000/', 24 | secure: false, 25 | changeOrigin: true 26 | } 27 | } 28 | }, 29 | 30 | // production 31 | clean: true, 32 | hash: true, 33 | sourceMap: false, 34 | publicPath: '/static/', 35 | assetsPath: 'images', 36 | urlLoaderLimit: 10000, 37 | extractCSS: 'css/[name].[contenthash:7].css', 38 | extends: ['vue2', 'eslint'] 39 | } 40 | if (process.env.NODE_ENV === 'production') { 41 | config.template = [{ 42 | filename: '../index.html', 43 | template: 'src/template/index.html', 44 | chunks: ['manifest', 'vendor', 'app'] 45 | }, { 46 | filename: '../login.html', 47 | template: 'src/template/login.html', 48 | chunks: ['manifest', 'vendor', 'login'] 49 | }] 50 | } else { 51 | config.template = [{ 52 | filename: 'index.html', 53 | template: 'src/template/index.html', 54 | chunks: ['vendor', 'app'] 55 | }, { 56 | filename: 'login.html', 57 | template: 'src/template/login.html', 58 | chunks: ['vendor', 'login'] 59 | }] 60 | } 61 | 62 | cooking.set(config) 63 | 64 | cooking.add('resolve.extensions', ['.js', '.vue', '.jsx']) 65 | cooking.add('resolve.alias', { 66 | 'src': path.join(__dirname, 'src') 67 | }) 68 | cooking.add('plugin.ProvidePlugin', new webpack.ProvidePlugin({$: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery'})) 69 | 70 | if (process.env.NODE_ENV === 'production') { 71 | cooking.add('output.filename', 'js/[name].[chunkhash:7].js') 72 | cooking.add('output.chunkFilename', 'js/[id].[chunkhash:7].js') 73 | cooking.add('plugin.CommonsChunk1', new webpack.optimize.CommonsChunkPlugin({ 74 | name: 'vendor', 75 | minChunks: function(module, count) { 76 | return (module.resource && /\.js$/.test(module.resource) && module.resource.indexOf('node_modules') > 0) 77 | } 78 | })) 79 | cooking.add('plugin.CommonsChunk2', new webpack.optimize.CommonsChunkPlugin({name: 'manifest', chunks: ['vendor']})) 80 | cooking.add('plugin.CopyWebpackPlugin', new CopyWebpackPlugin([{ 81 | from: 'favicon.ico', 82 | to: path.join(__dirname, 'dist') 83 | }, { 84 | from: 'static/editor.md/**/*', 85 | to: path.join(__dirname, 'dist') 86 | }])) 87 | } else { 88 | cooking.add('plugin.CommonsChunk', new webpack.optimize.CommonsChunkPlugin({ 89 | names: ["vendor"] 90 | })) 91 | } 92 | module.exports = cooking.resolve() 93 | -------------------------------------------------------------------------------- /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincenying/mmf-blog-vue2-jsx/30b25c41347189c6539420b59e779635003f2ff3/dist/favicon.ico -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | M·M·F 小屋 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /dist/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | M·M·F 小屋 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincenying/mmf-blog-vue2-jsx/30b25c41347189c6539420b59e779635003f2ff3/favicon.ico -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mmf-blog-vue2-jsx", 3 | "version": "2.0.0", 4 | "description": "mmf-blog vue2.0 jsx语法版 (vue2, vue-router, vuex)", 5 | "author": "lincenying ", 6 | "license": "MIT", 7 | "scripts": { 8 | "dev": "cooking watch -p", 9 | "build": "cooking build -p" 10 | }, 11 | "dependencies": { 12 | "jquery": "^3.1.1", 13 | "js-cookie": "^2.1.3", 14 | "nprogress": "^0.2.0", 15 | "store2": "^2.3.2", 16 | "toastr": "^2.1.2", 17 | "vue": "^2.1.6", 18 | "vue-router": "^2.1.1", 19 | "vuex": "^2.1.1", 20 | "vuex-router-sync": "^4.0.2" 21 | }, 22 | "devDependencies": { 23 | "babel-plugin-syntax-jsx": "^6.18.0", 24 | "babel-plugin-transform-vue-jsx": "^3.3.0", 25 | "copy-webpack-plugin": "^4.0.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | var autoprefixer = require('autoprefixer') 2 | var browserslist = require('browserslist') 3 | 4 | module.exports = { 5 | plugins: [autoprefixer({browsers: browserslist('last 2 version, > 0.1%')})] 6 | } 7 | -------------------------------------------------------------------------------- /src/LoginApp.jsx: -------------------------------------------------------------------------------- 1 | /* global window */ 2 | import './assets/css/login.css' 3 | import '../node_modules/toastr/build/toastr.css' 4 | import { mapGetters } from 'vuex' 5 | import ls from 'store2' 6 | import ajaxForm from './components/app/ajax-form' 7 | export default { 8 | computed: { 9 | ...mapGetters({ 10 | global: 'getGlobal' 11 | }) 12 | }, 13 | components: { 14 | ajaxForm 15 | }, 16 | data() { 17 | return { 18 | form: { 19 | username: '', 20 | password: '', 21 | remember_me: '' 22 | } 23 | } 24 | }, 25 | methods: { 26 | handleChange(type, e) { 27 | this.form[type] = e.target.value 28 | }, 29 | handleSubmit(e) { 30 | if (this.form.username === '' || this.form.password === '') { 31 | this.$store.dispatch('showMsg', '请输入用户名和密码') 32 | e.preventDefault() 33 | } 34 | }, 35 | handleComplete(res) { 36 | if (res.code === 200) { 37 | this.$store.dispatch('showMsg', { 38 | content: '登录成功', 39 | type: 'success' 40 | }) 41 | ls.set("token", res.data) 42 | setTimeout(() => { 43 | window.location.href = "/admin/post" 44 | }, 1000) 45 | } else { 46 | this.$store.dispatch('showMsg', res.message) 47 | } 48 | } 49 | }, 50 | render(h) { // eslint-disable-line 51 | return ( 52 |
53 | 67 |
68 | ) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | /* global $ */ 2 | import store from '../store' 3 | import config from '../config' 4 | 5 | $.ajaxSetup({ 6 | global: true, 7 | dataType: 'json', 8 | headers: { 9 | 'X-Requested-With': 'XMLHttpRequest', 10 | 'Authorization': 'Basic dGVzdDpwYXNzd2Q=' 11 | } 12 | }) 13 | 14 | $(document).ajaxStart(function() { 15 | store.dispatch('gProgress', 50) 16 | }) 17 | $(document).ajaxComplete(function() { 18 | store.dispatch('gProgress', 100) 19 | }) 20 | 21 | export default { 22 | get(url, data, global = true) { 23 | return new Promise((resolve, reject) => { 24 | $.ajax({ 25 | url: config.api + url, 26 | type: 'get', 27 | data, 28 | global 29 | }).then(data => { 30 | resolve(data) 31 | }, error => { 32 | store.dispatch('showMsg', error.responseText || error.statusText) 33 | reject(error) 34 | }) 35 | }) 36 | }, 37 | post(url, data, global = true) { 38 | return new Promise((resolve, reject) => { 39 | $.ajax({ 40 | url: config.api + url, 41 | type: 'post', 42 | data, 43 | global 44 | }).then(data => { 45 | resolve(data) 46 | }, error => { 47 | store.dispatch('showMsg', error.responseText || error.statusText) 48 | reject(error) 49 | }) 50 | }) 51 | }, 52 | } 53 | -------------------------------------------------------------------------------- /src/assets/css/hljs/github.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | github.com style (c) Vasily Polovnyov 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | color: #333; 12 | background: #f8f8f8; 13 | } 14 | 15 | .hljs-comment, 16 | .hljs-quote { 17 | color: #998; 18 | font-style: italic; 19 | } 20 | 21 | .hljs-keyword, 22 | .hljs-selector-tag, 23 | .hljs-subst { 24 | color: #333; 25 | font-weight: bold; 26 | } 27 | 28 | .hljs-number, 29 | .hljs-literal, 30 | .hljs-variable, 31 | .hljs-template-variable, 32 | .hljs-tag .hljs-attr { 33 | color: #008080; 34 | } 35 | 36 | .hljs-string, 37 | .hljs-doctag { 38 | color: #d14; 39 | } 40 | 41 | .hljs-title, 42 | .hljs-section, 43 | .hljs-selector-id { 44 | color: #900; 45 | font-weight: bold; 46 | } 47 | 48 | .hljs-subst { 49 | font-weight: normal; 50 | } 51 | 52 | .hljs-type, 53 | .hljs-class .hljs-title { 54 | color: #458; 55 | font-weight: bold; 56 | } 57 | 58 | .hljs-tag, 59 | .hljs-name, 60 | .hljs-attribute { 61 | color: #000080; 62 | font-weight: normal; 63 | } 64 | 65 | .hljs-regexp, 66 | .hljs-link { 67 | color: #009926; 68 | } 69 | 70 | .hljs-symbol, 71 | .hljs-bullet { 72 | color: #990073; 73 | } 74 | 75 | .hljs-built_in, 76 | .hljs-builtin-name { 77 | color: #0086b3; 78 | } 79 | 80 | .hljs-meta { 81 | color: #999; 82 | font-weight: bold; 83 | } 84 | 85 | .hljs-deletion { 86 | background: #fdd; 87 | } 88 | 89 | .hljs-addition { 90 | background: #dfd; 91 | } 92 | 93 | .hljs-emphasis { 94 | font-style: italic; 95 | } 96 | 97 | .hljs-strong { 98 | font-weight: bold; 99 | } 100 | -------------------------------------------------------------------------------- /src/assets/css/hljs/googlecode.css: -------------------------------------------------------------------------------- 1 | .hljs-comment, 2 | .hljs-quote { 3 | color: #800; 4 | } 5 | 6 | .hljs-keyword, 7 | .hljs-selector-tag, 8 | .hljs-section, 9 | .hljs-title, 10 | .hljs-name { 11 | color: #008; 12 | } 13 | 14 | .hljs-variable, 15 | .hljs-template-variable { 16 | color: #660; 17 | } 18 | 19 | .hljs-string, 20 | .hljs-selector-attr, 21 | .hljs-selector-pseudo, 22 | .hljs-regexp { 23 | color: #080; 24 | } 25 | 26 | .hljs-literal, 27 | .hljs-symbol, 28 | .hljs-bullet, 29 | .hljs-meta, 30 | .hljs-number, 31 | .hljs-link { 32 | color: #066; 33 | } 34 | 35 | .hljs-title, 36 | .hljs-doctag, 37 | .hljs-type, 38 | .hljs-attr, 39 | .hljs-built_in, 40 | .hljs-builtin-name, 41 | .hljs-params { 42 | color: #606; 43 | } 44 | 45 | .hljs-attribute, 46 | .hljs-subst { 47 | color: #000; 48 | } 49 | 50 | .hljs-formula { 51 | background-color: #eee; 52 | font-style: italic; 53 | } 54 | 55 | .hljs-selector-id, 56 | .hljs-selector-class { 57 | color: #9B703F 58 | } 59 | 60 | .hljs-addition { 61 | background-color: #baeeba; 62 | } 63 | 64 | .hljs-deletion { 65 | background-color: #ffc8bd; 66 | } 67 | 68 | .hljs-doctag, 69 | .hljs-strong { 70 | font-weight: bold; 71 | } 72 | 73 | .hljs-emphasis { 74 | font-style: italic; 75 | } 76 | -------------------------------------------------------------------------------- /src/assets/css/hljs/hljs.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:0.5em;background:#F0F0F0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888888}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-literal{color:#78A960}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} -------------------------------------------------------------------------------- /src/assets/images/arrow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincenying/mmf-blog-vue2-jsx/30b25c41347189c6539420b59e779635003f2ff3/src/assets/images/arrow.jpg -------------------------------------------------------------------------------- /src/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincenying/mmf-blog-vue2-jsx/30b25c41347189c6539420b59e779635003f2ff3/src/assets/images/icon.png -------------------------------------------------------------------------------- /src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincenying/mmf-blog-vue2-jsx/30b25c41347189c6539420b59e779635003f2ff3/src/assets/images/logo.png -------------------------------------------------------------------------------- /src/assets/images/w-icon-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincenying/mmf-blog-vue2-jsx/30b25c41347189c6539420b59e779635003f2ff3/src/assets/images/w-icon-1.png -------------------------------------------------------------------------------- /src/assets/images/w-icon-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincenying/mmf-blog-vue2-jsx/30b25c41347189c6539420b59e779635003f2ff3/src/assets/images/w-icon-2.png -------------------------------------------------------------------------------- /src/assets/images/w-icon-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincenying/mmf-blog-vue2-jsx/30b25c41347189c6539420b59e779635003f2ff3/src/assets/images/w-icon-3.png -------------------------------------------------------------------------------- /src/assets/images/w-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincenying/mmf-blog-vue2-jsx/30b25c41347189c6539420b59e779635003f2ff3/src/assets/images/w-icon.png -------------------------------------------------------------------------------- /src/components/about.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | methods: { 3 | handleSlideToggle() { 4 | $(".m-about").slideToggle("800") 5 | } 6 | }, 7 | render(h) { // eslint-disable-line 8 | return ( 9 |
10 | 14 |
15 | 18 |

姓名: 林岑影

19 |

年龄: 1987.09

20 |

职业: 前端开发

21 |

技能: HTML5 + CSS3 + jQuery + Gulp + WebPack + ES6 + Vue + NodeJS + PHP

22 | 收起个人介绍 23 |
24 |
25 | ) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/app/ajax-form.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | 'action' : { 4 | type : String, 5 | required: true 6 | }, 7 | 'method' : { 8 | type : String, 9 | required : true 10 | }, 11 | 'enctype': String, 12 | 'beforeFormSubmit': { 13 | type: Function, 14 | default() { 15 | return function(){} 16 | } 17 | }, 18 | 'onFormError': { 19 | type: Function, 20 | default() { 21 | return function(){} 22 | } 23 | }, 24 | 'onFormComplete': { 25 | type: Function, 26 | default() { 27 | return function(){} 28 | } 29 | }, 30 | 'onFormProgress': { 31 | type: Function, 32 | default() { 33 | return function(){} 34 | } 35 | }, 36 | 'afterFormSubmit': { 37 | type: Function, 38 | default() { 39 | return function(){} 40 | } 41 | } 42 | }, 43 | methods: { 44 | handleAjaxFormSubmit (e) { 45 | this.beforeFormSubmit() 46 | var handleError = err => { 47 | this.onFormError(err) 48 | } 49 | if (!this.method) { 50 | this.method = 'post' 51 | } 52 | // eslint-disable-next-line 53 | var xhr = new XMLHttpRequest() 54 | var handleFinish = () => { 55 | if (xhr.readyState === 4) { 56 | if (xhr.status < 400) { 57 | this.onFormComplete(xhr.response) 58 | } else { 59 | this.onFormError(xhr.statusText) 60 | } 61 | } 62 | } 63 | var handleProgress = evt => { 64 | if (evt.lengthComputable) { 65 | evt.percent = evt.loaded / evt.total * 100 66 | this.onFormProgress(evt) 67 | } 68 | } 69 | xhr.open(this.method, this.action, true) 70 | xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest") 71 | xhr.setRequestHeader('Authorization', 'Basic dGVzdDpwYXNzd2Q=') 72 | if (this.vResponseType) { 73 | xhr.responseType = this.vResponseType 74 | } else { 75 | xhr.responseType = 'json' 76 | } 77 | if (xhr.upload) xhr.upload.addEventListener('progress', handleProgress) 78 | xhr.addEventListener('readystatechange', handleFinish) 79 | xhr.addEventListener('error', handleError) 80 | xhr.addEventListener('abort', handleError) 81 | // eslint-disable-next-line 82 | var data = new FormData(event.target) 83 | xhr.send(data) 84 | this.afterFormSubmit() 85 | e.preventDefault() 86 | } 87 | }, 88 | render(h) { // eslint-disable-line 89 | return ( 90 |
91 | {this.$slots.default} 92 |
93 | ) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/components/index-post.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | props: ['item', 'ispc'], 3 | data () { 4 | return { 5 | showMore: false 6 | } 7 | }, 8 | methods: { 9 | handleOpen(e) { 10 | this.showMore = !this.showMore 11 | var $mPost = $(e.target).parents(".m-post") 12 | var offset = $mPost.offset() 13 | $("body").animate({ 14 | scrollTop: offset.top 15 | }, 500 ) 16 | } 17 | }, 18 | render(h) { // eslint-disable-line 19 | const more = !this.showMore ? 展开 ↓ : 收起 ↑ 20 | const moreOrLess = this.ispc ?
{more}
: '' 21 | return ( 22 |
23 |   24 |   25 | 28 |
29 |
30 |

{this.item.title}

31 | {this.ispc ?
: ''} 32 | {moreOrLess} 33 |
34 |
35 |
36 |
37 | ) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | api: '/api/' 3 | } 4 | -------------------------------------------------------------------------------- /src/login.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Login from './LoginApp.jsx' 3 | import store from './store' 4 | 5 | const app = new Vue({ 6 | store, 7 | ...Login 8 | }) 9 | 10 | app.$mount('#app') 11 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | import Vue from 'vue' 3 | import App from './App.jsx' 4 | import store from './store' 5 | import router from './router' 6 | import { sync } from 'vuex-router-sync' 7 | 8 | sync(store, router) 9 | 10 | const app = new Vue({ 11 | router, 12 | store, 13 | ...App 14 | }) 15 | 16 | router.beforeEach((to, from, next) => { 17 | store.dispatch('gProgress', 0) 18 | next() 19 | }) 20 | 21 | app.$mount('#app') 22 | -------------------------------------------------------------------------------- /src/pages/admin-list.jsx: -------------------------------------------------------------------------------- 1 | import { mapGetters } from 'vuex' 2 | const fetchInitialData = async store => { 3 | await store.dispatch('getAdminTopics', { 4 | limit: 20 5 | }) 6 | } 7 | export default { 8 | computed: { 9 | ...mapGetters({ 10 | topics: 'getAdminTopics' 11 | }), 12 | curPage() { 13 | return parseInt(this.$route.params.page, 10) 14 | }, 15 | prevPage() { 16 | return parseInt(this.$route.params.page, 10) - 1 17 | }, 18 | nextPage() { 19 | return parseInt(this.$route.params.page, 10) + 1 20 | } 21 | }, 22 | methods: { 23 | mdel(id) { 24 | this.$store.dispatch('deleteArticle', { 25 | id, 26 | action: 'delete' 27 | }) 28 | }, 29 | recover(id) { 30 | this.$store.dispatch('recoverArticle', { 31 | id, 32 | action: 'recover' 33 | }) 34 | } 35 | }, 36 | created() { 37 | if (this.$route.path !== this.topics.path) 38 | fetchInitialData(this.$store) 39 | else 40 | this.$store.dispatch('gProgress', 100) 41 | }, 42 | watch: { 43 | '$route'() { 44 | fetchInitialData(this.$store) 45 | } 46 | }, 47 | render(h) { // eslint-disable-line 48 | const lists = this.topics.list.map(item => { 49 | return ( 50 |
  • 51 | {item.title} 52 | { 53 | item.is_delete === 0 || item.is_delete === "0" ? 删除 : 恢复 54 | } 55 | 编辑 56 |
  • 57 | ) 58 | }) 59 | return ( 60 |
    61 |
    62 |
      63 | {lists} 64 |
    65 |
    66 |
    67 |
    68 |
    69 | { 70 | this.topics.hasPrev ? 71 | 上一页 : '' 72 | } 73 | { 74 | this.topics.hasNext ? 75 | 下一页 : '' 76 | } 77 |
    78 |
    79 | ) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/pages/admin-post.jsx: -------------------------------------------------------------------------------- 1 | /* global window, editormd, testEditor */ 2 | import config from '../config' 3 | import ajaxForm from '../components/app/ajax-form' 4 | export default { 5 | components: { 6 | ajaxForm 7 | }, 8 | data () { 9 | return { 10 | api: config.api + 'admin/article/post', 11 | editors: null, 12 | form: { 13 | title: '', 14 | category: '', 15 | content: '' 16 | } 17 | } 18 | }, 19 | methods: { 20 | handleChange(type, e) { 21 | this.form[type] = e.target.value 22 | }, 23 | handleSubmit(e) { 24 | if (this.form.title === '') { 25 | this.$store.dispatch('showMsg', '请输入标题') 26 | e.preventDefault() 27 | } else if (this.form.category === '') { 28 | this.$store.dispatch('showMsg', '请选择分类') 29 | e.preventDefault() 30 | } else if ($("#editor").val() === '') { 31 | this.$store.dispatch('showMsg', '请输入内容') 32 | e.preventDefault() 33 | } 34 | }, 35 | handleComplete(res) { 36 | this.$store.dispatch('showMsg', { 37 | content: res.message, 38 | type: res.code === 200 ? "success" : 'error' 39 | }) 40 | if (res.code === 200) { 41 | $("#article-post").get(0).reset() 42 | testEditor.clear() 43 | } 44 | } 45 | }, 46 | mounted() { 47 | window.testEditor = editormd("post-content", { 48 | width: "100%", 49 | height: 500, 50 | markdown: "", 51 | placeholder: '请输入内容...', 52 | path: '/static/editor.md/lib/', 53 | toolbarIcons() { 54 | return [ 55 | "bold", "italic", "quote", "|", 56 | "list-ul", "list-ol", "hr", "|", 57 | "link", "reference-link", "image", "code", "code-block", "table", "|", 58 | "watch", "preview", "fullscreen", "|", 59 | "help" 60 | ] 61 | }, 62 | watch : false, 63 | saveHTMLToTextarea : true, 64 | imageUpload : true, 65 | imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], 66 | imageUploadURL : "/api/?action=upload" 67 | }) 68 | this.$store.dispatch('gProgress', 100) 69 | }, 70 | render(h) { // eslint-disable-line 71 | return ( 72 |
    73 |
    74 | 75 |
    76 | 77 |
    78 |
    79 | 85 |
    86 |
    87 |