init

上级 ca207a2f
......@@ -23,3 +23,6 @@ package-lock.json
*.njsproj
*.sln
*.sw?
logs
run
# [4.8.0](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/4.6.0...4.8.0) (2019-11-08)
### Features
* ssr asyncData ([21e5b62](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/21e5b62))
* use easy-team mode ([473617a](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/473617a))
# [4.7.0](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/4.6.0...4.7.0) (2019-10-13)
### Features
* use easy-team mode ([473617a](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/473617a))
# [4.6.0](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/4.4.6...4.6.0) (2019-10-13)
### Bug Fixes
* server side render hydrate not effective ([8ef6aa8](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/8ef6aa8))
* server side render hydrate not effective ([7f1192f](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/7f1192f))
### Features
* add inject css and js demo ([11b6083](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/11b6083))
* inject script tag demoe ([713fb43](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/713fb43))
* load component on demand ([c95aecd](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/c95aecd))
* set title ([fb99718](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/fb99718))
* use easy-team mode ([36d74e3](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/36d74e3))
# [4.5.0](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/4.4.6...4.5.0) (2019-08-02)
### Bug Fixes
* server side render hydrate not effective ([8ef6aa8](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/8ef6aa8))
### Features
* set title ([fb99718](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/fb99718))
## [4.4.6](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/4.4.5...4.4.6) (2018-12-12)
### Bug Fixes
* not set baseUrl for axios ([d23a8c9](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/d23a8c9))
## [4.4.5](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/4.4.2...4.4.5) (2018-11-06)
### Bug Fixes
* https://github.com/easy-team/egg-vue-webpack-boilerplate/issues/88 ([cd47e3a](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/cd47e3a))
* https://github.com/easy-team/egg-vue-webpack-boilerplate/issues/99 ([4218f85](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/4218f85))
* imagemini plugin no install ([f80b54c](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/f80b54c))
### Features
* add link asset file example ([cb81976](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/cb81976))
# [4.4.0](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/4.3.0...4.4.0) (2018-10-09)
### Bug Fixes
* egg worker reload for config/manifest.json update ([c1df7f1](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/c1df7f1))
### Features
* simplified configuration ([1854282](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/1854282))
* webpack zero config implement ([16ea32e](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/16ea32e))
# [4.3.0](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/4.0.0...4.3.0) (2018-09-11)
### Bug Fixes
* add article and login style ([c510473](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/c510473))
* ajax cross origin ([d9998de](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/d9998de))
* article add and delete ([2a65c15](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/2a65c15))
* autoprefixer invalid ([4e497df](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/4e497df))
* dev local spa request error ([a79a408](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/a79a408))
* id params ([21d8b39](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/21d8b39))
* remove webpack4 compatible ([1734520](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/1734520))
### Features
* add component async load demo ([b8b3eaf](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/b8b3eaf))
* add i18n menu ([b7f65b0](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/b7f65b0))
* add npm clean command ([be4177d](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/be4177d))
* admin dashboard ([e63dc83](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/e63dc83))
* support i18n ([752baad](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/752baad))
* use egg-bin and egg-scripts ([cf85319](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/cf85319))
### Performance Improvements
* build by BABEL_ENV ([8d05f71](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/8d05f71))
# [4.0.0](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/3.5.0...4.0.0) (2018-03-07)
### Bug Fixes
* babel-eslint deps ([d7bb91a](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/d7bb91a))
* dll remove element-ui ([a58a5ec](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/a58a5ec))
* hack webpack, webpack issue 6681 ([e9ecb4c](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/e9ecb4c))
### Features
* add debug command ([aa7b344](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/aa7b344))
* add dynamic vue componnet demo ([1768ee0](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/1768ee0))
* dll ([f5c5679](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/f5c5679))
* dll and lib support ([bf0019c](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/bf0019c))
* spa dynamic import vue component ([a95b75f](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/a95b75f))
* spa use loader template ([09ea960](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/09ea960))
* support cli build dll and auto check dll file ([2f7e61a](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/2f7e61a))
* support html dll inject ([880cad4](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/880cad4))
* upgrade egg to 2.0.0 and node to 8 ([1313931](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/1313931))
* use .vue file as webpack entry ([9622d86](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/9622d86))
* use easywebpack env ([3d6ca7b](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/3d6ca7b))
* webpack 4 deps ([b245087](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/b245087))
* webpack4 ([2d58278](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/2d58278))
* webpack4 running ([a9e46e2](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/a9e46e2))
* webpack4 service worker adapter ([456ca48](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/456ca48))
## [3.2.2](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/v3.0.3...3.2.2) (2017-11-29)
### Bug Fixes
* element ui css link url ([f265956](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/f265956))
### Features
* support import css in js file ([b32a082](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/b32a082))
## [3.0.3](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/v3.0.2...v3.0.3) (2017-10-26)
## [3.0.2](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/v3.0.1...v3.0.2) (2017-10-26)
## [3.0.1](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/3.0.0...v3.0.1) (2017-10-26)
### Features
* add autoprefixer config ([be6837b](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/be6837b))
# [3.0.0](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/1.3.1...3.0.0) (2017-09-13)
## [1.3.1](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/1.2.0...1.3.1) (2017-08-29)
### Bug Fixes
* default node6 , node8 please see app/controller/await/await.js description ([75d0c57](https://github.com/easy-team/egg-vue-webpack-boilerplate/commit/75d0c57))
# [1.2.0](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/1.0.0...1.2.0) (2017-08-21)
# [1.0.0](https://github.com/easy-team/egg-vue-webpack-boilerplate/compare/0.6.0...1.0.0) (2017-05-25)
## 0.0.1 (2017-04-24)
The MIT License (MIT)
Copyright (c) 2017 sky.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# egg-vue-webpack-boilerplate
> 功能性需求或者Bug问题, 欢迎大家 PR 完善, 如果你需要了解更多信息,请加QQ群: 433207205(备注:Node.js).
基于 Egg + Vue + Webpack SSR 服务端渲染工程骨架项目,包括前台系统(SSR MPA)和后台管理系统(SSR SPA)。 如果要深入了解,建议请先阅读 https://www.yuque.com/easy-team/egg-vue 专题。
## 系统功能
### 前台博客系统 http://localhost:7001
**采用 Egg + Vue 服务端渲染**
![](https://github.com/easy-team/egg-vue-webpack-boilerplate/blob/master/docs/images/iblog.png?raw=true)
- 博客首页
- 文章列表
- 文章详情
### 后台管理系统 http://localhost:7001/admin
**采用 Egg + Vue + Vue-Router + Element 单页面服务端同构渲染**
![](https://github.com/hubcarl/egg-vue-webpack-boilerplate/blob/master/docs/images/admin.png?raw=true)
- Dashboard
- Markdown
- 文章管理
## 纯净版
因该项目包含了多种实现, 提供多种例子实现,为防干扰, 特提供了两个纯净版本分支用于实际项目开发, 请自行选用。
- Egg + Vue + Axios 多页面服务端渲染分支 [feature/green/multi](https://github.com/easy-team/egg-vue-webpack-boilerplate/tree/feature/green/multi)
- Egg + Vue + vue-router + vuex + axios 单页面服务端渲染分支 [feature/green/spa](https://github.com/easy-team/egg-vue-webpack-boilerplate/tree/feature/green/spa)
- Egg + Vue + vue-router + vuex + axios 前端渲染 asset 方案 [feature/green/asset](https://github.com/easy-team/egg-vue-webpack-boilerplate/tree/feature/green/asset)
- Egg + Vue + vue-router + vuex + axios 前端渲染一体化方案 [element-admin](https://github.com/easy-team/egg-vue-webpack-boilerplate/tree/element-admin)
- Egg + Vue + TypeScript 项目,请见项目 [egg-vue-typescript-boilerplate](https://github.com/easy-team/egg-vue-typescript-boilerplate)[ves-admin](https://github.com/easy-team/ves-admin)
以上项目,你可以通过 [easywebpack-cli](https://github.com/easy-team/easywebpack-cli) 初始化
## 版本
- Egg 版本: ^2.x.x
- Node 版本: ^8.x.x+
- Vue 版本: ^2.5.0
- Webpack 版本: ^4.x.x, 对应 `easywebpack-vue` 版本为 ^4.x.x; Webpack3 版本项目骨架请见 `webpack3` 分支, 对应 `easywebpack-react` 版本为 3.x.x
## 说明
- 项目开发之前, 请阅读[Egg + Vue 解决方案](https://www.yuque.com/easy-team/egg-vue)
- 如果你需要了解 Egg+ Vue + Webpack 项目更多信息,请扫以下二维码加好友,请备注:Node.js。 骨架功能性需求或者 Bug 问题, 欢迎大家 PR 完善。
![hubcarl](https://avatars3.githubusercontent.com/u/4983042?v=4&u=0befb64a57a7911c630b7f97df5632385b08da2a&s=250)
## 文档
- https://www.yuque.com/easy-team/egg-vue
- https://zhuanlan.zhihu.com/easywebpack
## 1.特性
- 支持服务端渲染SSR(Server Side Render), 前端渲染CSR(Client Side Render) 方式
- 支持 Node 和 前端代码修改, Webpack 自动编译和热更新, `npm run dev` 一键启动应用
- 基于 vue + axios 多页面服务端渲染, 客户端渲染同构实现, 支持 asyncData 渲染
- 基于 vue + vuex + vue-router + axios 单页面服务器客户端同构实现
- 支持 js/css/image 资源依赖, 内置支持 CDN 特性, 支持 css/sass/less 样式编写
- 支持根据 .vue 文件自动创建 Webpack Entry 入口文件
- 开始支持多进程和缓存编译, 支持 Webpack dll 自动化构建, 与多进程编译结合,构建速度减少 2/3
- 支持 Vue 组件 import 异步加载, 具体实例请看[app/web/page/dynamic](app/web/page/dynamic)
- 支持服务端渲染(SSR)失败时,自动降级为前端渲染(CSR)模式
- 提供 国际化 i18n 多语言支持方案
## 2.依赖
- [easywebpack](https://github.com/easy-team/easywebpack) ^4.x.x
- [easywebpack-vue](https://github.com/easy-team/easywebpack) ^4.x.x
- [egg-view-vue-ssr](https://github.com/easy-team/egg-view-vue-ssr) ^3.x.x
- [egg-webpack](https://github.com/easy-team/egg-webpack) ^4.x.x
- [egg-webpack-vue](https://github.com/easy-team/egg-webpack-vue) ^2.x.x
## 3. 使用
#### 3.1 安装cli(非必需)
```bash
npm install @easy-team/easywebpack-cli -g
```
`easywebpack-cli` 已内置 `devDependencies` 中, 无需安装。如果你需要在命令行使用 `easy` 命令, 可以单独全局安装。
#### 3.2 安装依赖
```bash
npm install
```
#### 3.3 本地开发启动应用
```bash
npm run dev
```
应用访问: http://127.0.0.1:7001
![npm start启动](https://github.com/hubcarl/egg-vue-webpack-boilerplate/blob/master/docs/images/webpack-build.png)
#### 3.4 发布模式启动应用
- 首先在本地或者ci构建好jsbundle文件
```bash
npm run build
```
- 然后,启动应用
```bash
npm start
```
详细打包部署请见: http://hubcarl.github.io/easywebpack/vue/dev/
## 4. 配置说明(支持三种方式)
#### 4.1 方式一: `easywebpack-cli` 根据 `webpack.config.js` 自动创建Webpack Config
```js
`config/config.local.js`
const EasyWebpack = require('@easy-team/easywebpack-vue');
exports.webpack = {
webpackConfigList:EasyWebpack.getWebpackConfig()
};
```
#### 4.2 方式二: 自己编写Webpack配置
编写配置请见 tag `1.0.0` build目录代码实现
```js
`config/config.local.js`
exports.webpack = {
webpackConfigList: [
require(path.join(app.baseDir, 'build/client')), // http://127.0.0.1:9000
]
};
```
#### 4.3 方式三: 开启多进程编译
[egg npm start 启动开启多进程编译](http://hubcarl.github.io/easywebpack/vue/version/)
构建会同时启动两个 webpack 构建服务, 客户端js构建(build/client), 服务端构建(build/server), 默认端口9000, webpackConfigList 端口依次递增.
## 5. 项目结构和基本规范
├── app
│   ├── controller
│   │   ├── test
│   │   │   └── test.js
│   ├── extend
│   ├── lib
│   ├── middleware
│   ├── mocks
│   ├── proxy
│   ├── router.js
│   ├── view
│   │ ├── about // 服务器编译的jsbundle文件
│   │   │   └── about.js
│   │   ├── home
│   │  │   └── home.js // 服务器编译的jsbundle文件
│   │   └── layout // 用于根据指定的layout生成对应的html页面, 用于服务器渲染失败时,采用客户端渲染
│   │   └── layout.html
│   └── web // 前端工程目录
│   ├── asset // 存放公共js,css资源
│   ├── framework // 前端公共库和第三方库
│   │   ├── fastclick
│   │   │   └── fastclick.js
│   │   ├── sdk
│   │   │   ├── sdk.js
│   │   ├── storage
│   │   │   └── storage.js
│   │   └── vue // 与vue相关的公开代码
│   │   ├── app.js // 前后端调用入口, 默认引入componet/directive/filter
│   │   ├── component.js // 组件入口, 可以增加component目录,类似下面的directive
│   │   ├── directive // directive 目录,存放各种directive组件
│   │   ├── directive.js // directive引用入口
│   │   └── filter.js // filter引用入口
│   ├── page // 前端页面和webpack构建目录, 也就是webpack打包配置entryDir
│   │   ├── home // 每个页面遵循目录名, js文件名, scss文件名, vue文件名相同
│   │   │   ├── home.scss
│   │   │   ├── home.vue
│   │   │   ├── images // 页面自有图片,公共图片和css放到asset下面
│   │   │   │   └── icon_more.png
│   │   │   └── w-week // 页面自有组件,公共组件放到widget下面
│   │   │   ├── w-week.scss
│   │   │   └── w-week.vue
│   │   └── test // 每个页面遵循目录名, js文件名, scss文件名, vue文件名相同
│   │   └── test.vue
│   ├── store // 引入vuex 的基本规范, 可以分模块
│   │   ├── app
│   │   │   ├── actions.js
│   │   │   ├── getters.js
│   │   │   ├── index.js
│   │   │   ├── mutation-type.js
│   │   │   └── mutations.js
│   │   └── store.js
│   └── component // 公共业务组件, 比如loading, toast等, 遵循目录名, js文件名, scss文件名, vue文件名相同
│   ├── loading
│   │   ├── loading.scss
│   │   └── loading.vue
│   ├── test
│   │   ├── test.vue
│   │   └── test.scss
│   └── toast
│   ├── toast.scss
│   └── toast.vue
├── build // webpack 自定义配置入口, 会与默认配置进行合并(看似这么多,其实这里只是占个位说明一下)
│   ├── base
│   │  └── index.js // 公共配置
│   ├── client // 客户端webpack编译配置
│   │ ├── dev.js
│ │ ├── prod.js
│   │ └── index.js
│ ├── server // 服务端webpack编译配置
│   │ ├── dev.js
│ │ ├── prod.js
│   │ └── index.js
│ └── index.js
├── config
│   ├── config.default.js
│   ├── config.local.js
│   ├── config.prod.js
│   ├── config.test.js
│   └── plugin.js
├── doc
├── index.js
├── public // webpack编译目录结构, render文件查找目录
│   ├── manifest.json // 资源依赖表
│   ├── static
│   │   ├── css
│   │   │   ├── home
│   │   │   │   ├── home.07012d33.css
│   │   │   └── test
│   │   │   ├── test.4bbb32ce.css
│   │   ├── img
│   │   │   ├── change_top.4735c57.png
│   │   │   └── intro.0e66266.png
│   ├── test
│   │   └── test.js
│   └── vendor.js // 生成的公共打包库
## 6. 功能实现
支持多页面/单页面服务端渲染, 前端渲染, 静态页面三种方式.
### 6.1 多页面服务端渲染/前端渲染同构实现
#### 6.1.1 多页面前端页面实现
在app/web/page 目录下面创建home目录, home.vue 文件, Webpack自动根据.vue文件创建entry入口, 具体实现请见[webpack.config.js](webpack.config.js)
- home.vue 编写界面逻辑, 根元素为layout(自定义组件, 全局注册, 统一的html, meta, header, body)
```html
<template>
<layout title="基于egg-vue-webpack-dev和egg-view-vue插件的工程示例项目" description="vue server side render" keywords="egg, vue, webpack, server side render">
{{message}}
</layout>
</template>
<style>
@import "home.css";
</style>
<script type="text/babel">
export default {
components: {
},
computed: {
},
methods: {
},
mounted() {
}
}
</script>
```
#### 6.1.2 多页面后端渲染实现, 通过 `egg-view-vue-ssr` 插件 `render` 方法实现
- 创建controller文件home.js
```javascript
exports.index = function* (ctx) {
yield ctx.render('home/home.js', { message: 'vue server side render!' });
};
```
- 添加路由配置
```javascript
app.get('/home', app.controller.home.home.index);
```
#### 6.1.3 多页面走前端渲染(后端路由)实现, 通过 `egg-view-vue-ssr` 插件 `renderClient` 方法实现
- 创建controller文件home.js
```javascript
exports.client = function* (ctx) {
yield ctx.renderClient('home/home.js', { message: 'vue server side render!' });
};
```
- 添加路由配置
```javascript
app.get('/client', app.controller.home.home.client);
```
#### 6.1.4 HTML静态页面前端渲染
- 直接有easywebpack构建出静态HTML文件, 请见 `webpack.config.js` 配置和 `app/web/page/html`代码实现
- 通过 `egg-static` 静态文件访问HTML文件
### 6.2 单页面服务器渲染同构实现
#### 6.2.1 单页面前端实现
在app/web/page 目录下面创建app目录, app.vue, app.js 文件.
- app.vue 编写界面逻辑, 根元素为layout(自定义组件, 全局注册, 统一的html, meta, header, body)
```html
<template>
<app-layout>
<transition name="fade" mode="out-in">
<router-view></router-view>
</transition>
</app-layout>
</template>
<style lang="sass">
</style>
<script type="text/babel">
export default {
computed: {
},
mounted(){
}
}
</script>
```
- app.js 页面调用入口
```javascript
import { sync } from 'vuex-router-sync';
import store from 'store/app';
import router from 'component/app/router';
import app from './app.vue';
import App from 'app';
import Layout from 'component/layout/app';
App.component(Layout.name, Layout);
sync(store, router);
export default App.init({
base: '/app',
...app,
router,
store
});
```
#### 6.2.2 单页面后端实现
- 创建controller文件app.js
```javascript
exports.index = function* (ctx) {
yield ctx.render('app/app.js', { url: this.url.replace(/\/app/, '') });
};
```
- 添加路由配置
```javascript
app.get('/app(/.+)?', app.controller.app.app.index);
```
## License
[MIT](LICENSE)
'use strict';
const egg = require('egg');
module.exports = class AdminController extends egg.Controller {
async login(ctx) {
await ctx.renderClient('admin/login/login.js', {});
}
async home(ctx) {
const url = ctx.url.replace(/\/admin/, '');
const { mode } = ctx.query;
if (mode === 'csr') {
await ctx.renderClient('admin/home/home.js', { ctx, url, title: 'easy-admin' });
} else {
await ctx.render('admin/home/home.js', { ctx, url, title: 'easy-admin' });
}
}
async list(ctx) {
this.ctx.body = ctx.service.article.getArtilceList(ctx.request.body);
}
async add(ctx) {
ctx.body = this.service.article.saveArticle(ctx.request.body);
}
async del(ctx) {
const { id } = ctx.params;
ctx.body = this.service.article.deleteArticle(id);
}
async detail(ctx) {
const id = ctx.params.id;
console.log('>>id', Number(id));
ctx.body = this.service.article.getArticle(Number(id));
}
};
\ No newline at end of file
'use strict';
module.exports = app => {
return class CategoryController extends app.Controller {
async index(ctx) {
await ctx.render('category/category.js', { message: 'Egg Vue Server Side Render: Category' });
}
};
};
\ No newline at end of file
'usestrict';
const egg = require('egg');
module.exports = class IndexController extends egg.Controller {
async ssr() {
const result = this.service.article.getArtilceList();
await this.ctx.render('index/index.js', result);
}
async csr() {
const result = this.service.article.getArtilceList();
await this.ctx.renderClient('index/index.js', result);
}
async list() {
this.ctx.body = this.service.article.getArtilceList(this.ctx.query);
}
async detail() {
const id = this.ctx.query.id;
}
};
\ No newline at end of file
'use strict';
const Factory = require('../lib/db/factory');
const DBSymbol = Symbol('Application#db');
module.exports = {
get db() {
if (!this[DBSymbol]) {
this[DBSymbol] = Factory();
}
return this[DBSymbol];
},
};
\ No newline at end of file
'use strict';
module.exports = {
get db() {
return this.app.db;
}
};
\ No newline at end of file
'use strict';
const shortid = require('shortid');
module.exports = class DB {
constructor(name = 'blog.json') {
this.name = name;
}
getUniqueId() {
return shortid.generate();
}
get(collectionName) {
return null;
}
add(collectionName, json) {
return null;
}
update(collectionName, where, json) {
return null;
}
delete(collectionName, field) {
return null;
}
getPager(collectionName, query) {
return null;
}
};
\ No newline at end of file
'use strict';
module.exports = class Collection {
constructor(db, name) {
this.db = db;
this.name = name;
}
get() {
return this.db.get(this.name);
}
getByWhere(json) {
return this.db.get(this.name).find(json);
}
add(json) {
return this.db.add(this.name, json);
}
update(where, json) {
return this.db.update(this.name, where, json);
}
delete(field) {
return this.db.delete(this.name, field);
}
getPager(query) {
return this.db.getPager(this.name, query);
}
getOrderAscByField(field) {
return this.get().orderBy(field, 'asc').value();
}
getOrderDescByField(field) {
return this.get().orderBy(field, 'desc').value();
}
};
\ No newline at end of file
'use strict';
const File = require('./file');
const MySQL = require('./mysql');
const MongoDB = require('./mongo');
module.exports = type => {
switch (type) {
case 'mysql':
return new MySQL();
case 'mongo':
return new MongoDB();
default:
return new File();
}
};
\ No newline at end of file
'use strict';
const lowdb = require('lowdb');
const lodashid = require('lodash-id');
const FileSync = require('lowdb/adapters/FileSync');
const Base = require('./base');
module.exports = class FileDB extends Base {
constructor(name) {
super(name);
const file = new FileSync(this.name);
this.instance = lowdb(file);
this.instance._.mixin(lodashid);
this.create();
}
create() {
this.instance.defaults({
article: [],
user: {}
}).write();
}
get(collectionName) {
return this.instance.get(collectionName);
}
add(collectionName, json) {
return this.get(collectionName)
.push(json)
.write();
}
update(collectionName, where, json) {
return this.get(collectionName).find(where).assign(json).write();
}
delete(collectionName, field) {
return this.get(collectionName).remove(field).write();
}
getPager(collectionName, json) {
const {
where,
like,
pageIndex,
pageSize,
orderByField,
orderBy
} = json;
const start = (pageIndex - 1) * pageSize;
const end = pageIndex * pageSize;
const result = this.get(collectionName)
.filter(where)
.filter(item => {
return Object.keys(like).reduce((isLike, key) => {
return isLike && item[key] && item[key].indexOf(like[key]) > -1;
}, true);
})
.orderBy(orderByField, orderBy);
const total = result.size().value();
const list = result.slice(start, end).value();
return {
total,
list
};
}
};
\ No newline at end of file
'use strict';
const Base = require('./base');
module.exports = class MongoDB extends Base {};
\ No newline at end of file
'use strict';
const Base = require('./base');
module.exports = class MySQLDB extends Base {};
'use strict';
module.exports = class Query {
constructor() {
this.orderByField = 'createTime';
this.orderBy = 'desc';
this._pageIndex = 1;
this._pageSize = 10;
this.where = new Proxy({}, {
set(target, key, value, proxy) {
if (value === undefined) {
return true;
}
return Reflect.set(target, key, value, proxy);
}
});
this.like = new Proxy({}, {
set(target, key, value, proxy) {
if (value === undefined) {
return true;
}
return Reflect.set(target, key, value, proxy);
}
});
}
get pageIndex() {
return this._pageIndex;
}
set pageIndex(value) {
if (value !== undefined) {
this._pageIndex = value;
}
}
get pageSize() {
return this._pageSize;
}
set pageSize(value) {
if (value !== undefined) {
this._pageSize = value;
}
}
};
\ No newline at end of file
const path = require('path');
const util = require('util');
module.exports = () => {
const skipExt = [ '.png', '.jpeg', '.jpg', '.ico', '.gif' ];
return function* (next) {
const start = new Date().getTime();
yield* next;
const rs = Math.ceil(new Date().getTime() - start);
this.set('X-Response-Time', rs);
const ext = path.extname(this.url).toLocaleLowerCase();
const isSkip = skipExt.indexOf(ext) !== -1 && this.status < 400;
if (!isSkip) {
const ip = this.get('X-Real-IP') || this.ip;
const port = this.get('X-Real-Port');
const protocol = this.protocol.toUpperCase();
const method = this.method;
const url = this.url;
const status = this.status;
const length = this.length || '-';
const referrer = this.get('referrer') || '-';
const ua = this.get('user-agent') || '-';
const serverTime = this.response.get('X-Server-Response-Time') || '-';
const message = util.format('[access] %s:%s - %s %s %s/%s %s %s %s %s %s',
ip, port, method, url, protocol, status, length, referrer, rs, serverTime, ua);
this.logger.info(message);
}
};
};
module.exports = () => {
return async function locale(ctx, next) {
ctx.locals.locale = ctx.query.locale || 'cn';
ctx.locals.origin = ctx.request.origin;
await next();
};
};
\ No newline at end of file
'use strict';
module.exports = class Model {
constructor() {
super();
this.id = void 0;
this.title = undefined;
this.summary = undefined;
this.tag = undefined;
this.hits = 0;
this.createTime = Date.now();
}
};
\ No newline at end of file
'use strict';
module.exports = class ArticleDetail {};
'use strict';
module.exports = class User {
constructor() {
this.id = null;
this.name = null;
this.password = null;
this.roleId = null;
}
};
\ No newline at end of file
'use strict';
const Pagination = require('./pagination');
module.exports = class Model {
constructor() {
this.pagination = new Pagination();
}
};
\ No newline at end of file
'use strict';
module.exports = app => {
const { router, controller } = app;
router.get('/', controller.index.index.ssr);
router.get('/csr', controller.index.index.csr);
router.get('/list', controller.index.index.list);
router.get('/category', controller.category.category.index);
router.get('/login', controller.admin.admin.login);
router.post('/admin/api/article/list', controller.admin.admin.list);
router.post('/admin/api/article/add', controller.admin.admin.add);
router.get('/admin/api/article/del/:id', controller.admin.admin.del);
router.get('/admin/api/article/:id', controller.admin.admin.detail);
router.get('/admin(/.+)?', controller.admin.admin.home);
};
\ No newline at end of file
'use strict';
const egg = require('egg');
const Collection = require('../lib/db/collection');
const Query = require('../lib/db/query');
module.exports = class ArticeService extends egg.Service {
constructor(ctx) {
super(ctx);
this.ctx = ctx;
this.colllection = new Collection(ctx.db, 'article');
}
getArtilceList(json = {}) {
const { title, categoryId, status, pageIndex, pageSize } = json;
const query = new Query();
query.where.categoryId = categoryId;
query.where.status = status;
query.like.title = title;
query.pageIndex = pageIndex;
query.pageSize = pageSize;
return this.colllection.getPager(query);
}
getArticle(id) {
return this.colllection.getByWhere({ id });
}
saveArticle(json) {
if (json.id) {
return this.colllection.update({ id: json.id }, json);
}
json.id = this.ctx.db.getUniqueId();
this.colllection.add(json);
return json.id;
}
deleteArticle(id) {
return this.colllection.delete({ id });
}
};
\ No newline at end of file
## egg规范view目录, 保证view文件夹存在, 否则app.config.view.root为空, 编译服务器文件会存放到该目录.
\ No newline at end of file
body {
font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", "STHeiti", "Microsoft YaHei", "WenQuanYi Micro Hei", SimSun, sans-serif;
line-height: 1.7;
font-size: 16px;
color: #404040;
overflow-x: hidden;
}
p {
margin: 30px 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", "STHeiti", "Microsoft YaHei", "WenQuanYi Micro Hei", SimSun, sans-serif;
line-height: 1.1;
font-weight: 800;
}
h1 {
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
h4 {
font-size: 18px;
margin-top: 24px;
margin-bottom: 24px;
}
a {
color: #404040;
}
a:hover,
a:focus {
color: #0085a1;
}
a img:hover,
a img:focus {
cursor: zoom-in;
}
article {
overflow-x: hidden;
}
blockquote {
color: #808080;
font-style: italic;
font-size: 0.95em;
margin: 20px 0 20px;
}
blockquote p {
margin: 0;
}
@media screen and (max-width: 768px) {
select {
-webkit-appearance: none;
margin-top: 15px;
color: #44bff2;
border-color: #44bff2;
padding: 0em 0.4em;
background: white;
}
}
.table th,
.table td {
border: 1px solid #eee !important;
}
hr.small {
max-width: 100px;
margin: 15px auto;
border-width: 4px;
border-color: white;
}
hr.large {
max-width: 280px;
margin: 15px auto;
border-width: 4px;
border-color: white;
}
@media screen and (max-width: 500px) {
pre code {
display: block;
width: 500px;
}
}
.post-container a {
color: #2B4180;
}
.post-container a:hover,
.post-container a:focus {
color: #6bb4bf;
}
.post-container ul,
.post-container ol {
margin-bottom: 40px;
}
.post-container ol ol,
.post-container ol ul,
.post-container ul ol,
.post-container ul ul {
margin-bottom: 5px;
}
.post-container li p {
margin: 0;
margin-bottom: 5px;
}
.post-container li h1,
.post-container li h2,
.post-container li h3,
.post-container li h4,
.post-container li h5,
.post-container li h6 {
line-height: 2;
margin-top: 20px;
}
.navbar-custom {
background: white;
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 3;
font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", "STHeiti", "Microsoft YaHei", "WenQuanYi Micro Hei", SimSun, sans-serif;
line-height: 1.7;
}
.navbar-custom .navbar-brand {
font-weight: 800;
}
.navbar-custom .nav li a {
text-transform: uppercase;
font-size: 16px;
font-weight: 800;
letter-spacing: 1px;
}
@media only screen and (min-width: 768px) {
.navbar-custom {
background: transparent;
border-bottom: 1px solid transparent;
}
.navbar-custom body {
font-size: 20px;
}
.navbar-custom .navbar-brand {
color: white;
padding: 20px;
}
.navbar-custom .navbar-brand:hover,
.navbar-custom .navbar-brand:focus {
color: rgba(255, 255, 255, 0.8);
}
.navbar-custom .nav li a {
color: white;
padding: 20px;
}
.navbar-custom .nav li a:hover,
.navbar-custom .nav li a:focus {
color: rgba(255, 255, 255, 0.8);
}
}
@media only screen and (min-width: 1170px) {
.navbar-custom {
-webkit-transition: background-color 0.3s;
-moz-transition: background-color 0.3s;
transition: background-color 0.3s;
/* Force Hardware Acceleration in WebKit */
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-ms-transform: translate3d(0, 0, 0);
-o-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.navbar-custom.is-fixed {
/* when the user scrolls down, we hide the header right above the viewport */
position: fixed;
top: -61px;
background-color: rgba(255, 255, 255, 0.9);
border-bottom: 1px solid #f2f2f2;
-webkit-transition: -webkit-transform 0.3s;
-moz-transition: -moz-transform 0.3s;
transition: transform 0.3s;
}
.navbar-custom.is-fixed .navbar-brand {
color: #404040;
}
.navbar-custom.is-fixed .navbar-brand:hover,
.navbar-custom.is-fixed .navbar-brand:focus {
color: #0085a1;
}
.navbar-custom.is-fixed .nav li a {
color: #404040;
}
.navbar-custom.is-fixed .nav li a:hover,
.navbar-custom.is-fixed .nav li a:focus {
color: #0085a1;
}
.navbar-custom.is-visible {
/* if the user changes the scrolling direction, we show the header */
-webkit-transform: translate3d(0, 100%, 0);
-moz-transform: translate3d(0, 100%, 0);
-ms-transform: translate3d(0, 100%, 0);
-o-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
}
}
.intro-header {
background: no-repeat center center;
background-color: #808080;
background-attachment: scroll;
-webkit-background-size: cover;
-moz-background-size: cover;
background-size: cover;
-o-background-size: cover;
margin-bottom: 0px;
/* 0 on mobile, changed the setting*/
}
@media only screen and (min-width: 768px) {
.intro-header {
margin-bottom: 20px;
/* response on desktop */
}
}
.intro-header .site-heading,
.intro-header .post-heading,
.intro-header .page-heading {
padding: 100px 0 50px;
color: white;
}
@media only screen and (min-width: 768px) {
.intro-header .site-heading,
.intro-header .post-heading,
.intro-header .page-heading {
padding: 150px 0;
}
}
.intro-header .site-heading,
.intro-header .page-heading {
text-align: center;
}
.intro-header .site-heading h1,
.intro-header .page-heading h1 {
margin-top: 0;
font-size: 36px;
}
.intro-header .site-heading .subheading,
.intro-header .page-heading .subheading {
font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", "STHeiti", "Microsoft YaHei", "WenQuanYi Micro Hei", SimSun, sans-serif;
line-height: 1.7;
font-size: 20px;
line-height: 1.1;
display: block;
font-weight: 300;
margin: 10px 0 0;
}
@media only screen and (min-width: 768px) {
.intro-header .site-heading h1,
.intro-header .page-heading h1 {
font-size: 65px;
}
}
.intro-header .post-heading h1 {
font-size: 27px;
}
.intro-header .post-heading .subheading,
.intro-header .post-heading .meta {
line-height: 1.1;
display: block;
}
.intro-header .post-heading .subheading {
font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", "STHeiti", "Microsoft YaHei", "WenQuanYi Micro Hei", SimSun, sans-serif;
line-height: 1.7;
font-size: 17px;
font-weight: normal;
margin: 10px 0 30px;
margin-top: -5px;
}
.intro-header .post-heading .meta {
font-family: 'Lora', 'Times New Roman', serif;
font-style: italic;
font-weight: 300;
font-size: 17px;
}
.intro-header .post-heading .meta a {
color: white;
}
@media only screen and (min-width: 768px) {
.intro-header .post-heading h1 {
font-size: 36px;
}
.intro-header .post-heading .subheading {
font-size: 30px;
}
.intro-header .post-heading .meta {
font-size: 20px;
}
}
.post-preview > a {
color: #404040;
}
.post-preview > a:hover,
.post-preview > a:focus {
text-decoration: none;
color: #0666C5;
}
.post-preview > a > .post-title {
font-size: 21px;
line-height: 1.3;
margin-top: 30px;
margin-bottom: 8px;
}
.post-preview > a > .post-subtitle {
font-size: 15px;
margin: 0;
font-weight: 300;
margin-bottom: 10px;
}
.post-preview > .post-meta {
font-family: 'Lora', 'Times New Roman', serif;
color: #808080;
font-size: 16px;
font-style: italic;
margin-top: 0;
}
.post-preview > .post-meta > a {
text-decoration: none;
color: #404040;
}
.post-preview > .post-meta > a:hover,
.post-preview > .post-meta > a:focus {
color: #2B4180;
text-decoration: underline;
}
@media only screen and (min-width: 768px) {
.post-preview > a > .post-title {
font-size: 26px;
line-height: 1.3;
margin-bottom: 10px;
}
.post-preview > a > .post-subtitle {
font-size: 16px;
}
.post-preview .post-meta {
font-size: 18px;
}
}
.post-content-preview {
font-size: 13px;
font-style: normal;
color: #a3a3a3;
}
.post-content-preview:hover {
color: #46A5ED;
}
@media only screen and (min-width: 768px) {
.post-content-preview {
font-size: 14px;
}
}
.section-heading {
font-size: 36px;
margin-top: 60px;
font-weight: 700;
}
.caption {
text-align: center;
font-size: 14px;
padding: 10px;
font-style: italic;
margin: 0;
display: block;
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
}
footer {
font-size: 20px;
padding: 50px 0 65px;
}
footer .list-inline {
margin: 0;
padding: 0;
}
footer .copyright {
font-size: 14px;
text-align: center;
margin-bottom: 0;
}
footer .copyright a {
color: #93e1de;
}
footer .copyright a:hover,
footer .copyright a:focus {
color: #44bff2;
}
.floating-label-form-group {
font-size: 14px;
position: relative;
margin-bottom: 0;
padding-bottom: 0.5em;
border-bottom: 1px solid #eeeeee;
}
.floating-label-form-group input,
.floating-label-form-group textarea {
z-index: 1;
position: relative;
padding-right: 0;
padding-left: 0;
border: none;
border-radius: 0;
font-size: 1.5em;
background: none;
box-shadow: none !important;
resize: none;
}
.floating-label-form-group label {
display: block;
z-index: 0;
position: relative;
top: 2em;
margin: 0;
font-size: 0.85em;
line-height: 1.764705882em;
vertical-align: middle;
vertical-align: baseline;
opacity: 0;
-webkit-transition: top 0.3s ease,opacity 0.3s ease;
-moz-transition: top 0.3s ease,opacity 0.3s ease;
-ms-transition: top 0.3s ease,opacity 0.3s ease;
transition: top 0.3s ease,opacity 0.3s ease;
}
.floating-label-form-group::not(:first-child) {
padding-left: 14px;
border-left: 1px solid #eeeeee;
}
.floating-label-form-group-with-value label {
top: 0;
opacity: 1;
}
.floating-label-form-group-with-focus label {
color: #2B4180;
}
form .row:first-child .floating-label-form-group {
border-top: 1px solid #eeeeee;
}
.btn {
font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", "STHeiti", "Microsoft YaHei", "WenQuanYi Micro Hei", SimSun, sans-serif;
line-height: 1.7;
text-transform: uppercase;
font-size: 14px;
font-weight: 800;
letter-spacing: 1px;
border-radius: 0;
padding: 15px 25px;
}
.btn-lg {
font-size: 16px;
padding: 25px 35px;
}
.btn-default:hover,
.btn-default:focus {
background-color: #0085a1;
border: 1px solid #0085a1;
color: white;
}
.pager {
margin: 20px 0 0;
}
.pager li > a,
.pager li > span {
font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", "STHeiti", "Microsoft YaHei", "WenQuanYi Micro Hei", SimSun, sans-serif;
line-height: 1.7;
text-transform: uppercase;
font-size: 14px;
font-weight: 800;
letter-spacing: 1px;
padding: 10px 5px;
background-color: white;
border-radius: 0;
}
@media only screen and (min-width: 768px) {
.pager li > a,
.pager li > span {
font-size: 14px;
padding: 15px 25px;
}
}
.pager li > a {
color: #404040;
}c
.pager li > a:hover,
.pager li > a:focus {
color: white;
background-color: #00c9d7;
border: 1px solid #00c9d7;
}
.pager .disabled > a,
.pager .disabled > a:hover,
.pager .disabled > a:focus,
.pager .disabled > span {
color: #808080;
background-color: #404040;
cursor: not-allowed;
}
::-moz-selection {
color: white;
text-shadow: none;
background: #00c9d7;
}
::selection {
color: white;
text-shadow: none;
background: #00c9d7;
}
img::selection {
color: white;
background: transparent;
}
img::-moz-selection {
color: white;
background: transparent;
}
body {
webkit-tap-highlight-color: #00c9d7;
}
/* Optimize UserExperience */
.navbar-default .navbar-toggle:focus,
.navbar-default .navbar-toggle:hover {
background-color: inherit;
}
.navbar-default .navbar-toggle:active {
background-color: #ddd;
}
此差异已折叠。
此差异已折叠。
* {
padding: 0;
margin: 0;
outline: none;
box-sizing: border-box;
}
html, body{
height: 100%;
}
a {
color: #3c8dbc;
text-decoration: none;
}
-webkit-scrollbar {
width: 4px;
background-color: #F5F5F5;
}
-webkit-scrollbar-track {
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
background-color: #F5F5F5;
}
-webkit-scrollbar-thumb {
box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
background-color: #3e8dbb;
}
\ No newline at end of file
此差异已折叠。
此差异已折叠。
<template>
<div class="simplemde-container" :style="{height:height+'px',zIndex:zIndex}">
<textarea :id="id">
</textarea>
</div>
</template>
<script>
import 'font-awesome/css/font-awesome.min.css'
import 'simplemde/dist/simplemde.min.css'
import SimpleMDE from 'simplemde'
export default {
name: 'simplemde-md',
props: {
value: String,
id: {
type: String
},
autofocus: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: ''
},
height: {
type: Number,
default: 400
},
zIndex: {
type: Number,
default: 10
},
toolbar: {
type: Array
}
},
data() {
return {
simplemde: null,
hasChange: false
}
},
watch: {
value(val) {
if (val === this.simplemde.value() && !this.hasChange) return
this.simplemde.value(val)
}
},
mounted() {
this.simplemde = new SimpleMDE({
element: document.getElementById(this.id || 'markdown-editor-' + +new Date()),
autoDownloadFontAwesome: false,
autofocus: this.autofocus,
toolbar: this.toolbar,
spellChecker: false,
insertTexts: {
link: ['[', ']( )']
},
// hideIcons: ['guide', 'heading', 'quote', 'image', 'preview', 'side-by-side', 'fullscreen'],
placeholder: this.placeholder
})
if (this.value) {
this.simplemde.value(this.value)
}
this.simplemde.codemirror.on('change', () => {
if (this.hasChange) {
this.hasChange = true
}
this.$emit('input', this.simplemde.value())
})
},
destroyed() {
this.simplemde.toTextArea()
this.simplemde = null
}
}
</script>
<style scoped>
.simplemde-container>>>.CodeMirror {
min-height: 150px;
line-height: 20px;
}
.simplemde-container>>>.CodeMirror-scroll {
min-height: 450px;
}
.simplemde-container>>>.CodeMirror-code {
padding-bottom: 40px;
}
.simplemde-container>>>.editor-statusbar {
display: none;
}
.simplemde-container>>>.CodeMirror .CodeMirror-code .cm-link {
color: #1890ff;
}
.simplemde-container>>>.CodeMirror .CodeMirror-code .cm-string.cm-url {
color: #2d3b4d;
}
.simplemde-container>>>.CodeMirror .CodeMirror-code .cm-formatting-link-string.cm-url {
padding: 0 2px;
color: #E61E1E;
}
.simplemde-container >>> .editor-toolbar.fullscreen,
.simplemde-container >>> .CodeMirror-fullscreen {
z-index: 1003;
}
</style>
.main{
margin-top: -80px;
padding: 80px 0 120px;
box-sizing: border-box;
}
.page-container{
width: 1140px;
padding: 0 30px;
margin: 36 auto;
}
.page-component {
padding-bottom: 95px;
box-sizing: border-box
}
.page-component .content {
margin-left: -1px
}
.page-component .content > h3 {
margin: 45px 0 15px
}
.page-component .content > table {
border-collapse: collapse;
width: 100%;
background-color: #fff;
color: #5e6d82;
font-size: 14px;
margin-bottom: 45px
}
.page-component .content > table strong {
font-weight: 400
}
.page-component .content > table th {
text-align: left;
border-top: 1px solid #eaeefb;
background-color: #eff2f7;
white-space: nowrap
}
.page-component .content > table td, .page-component .content > table th {
border-bottom: 1px solid #eaeefb;
padding: 10px;
max-width: 250px
}
.page-component .content > table td:first-child, .page-component .content > table th:first-child {
padding-left: 10px
}
.page-component .page-component-up {
background-color: #58b7ff;
position: fixed;
right: 100px;
bottom: 150px;
width: 50px;
height: 50px;
border-radius: 25px;
cursor: pointer;
opacity: .4;
transition: .3s
}
.page-component .page-component-up i {
color: #fff;
display: block;
line-height: 50px;
text-align: center;
font-size: 22px
}
.page-component .page-component-up.hover {
opacity: 1
}
.page-component .back-top-fade-enter, .page-component .back-top-fade-leave-active {
transform: translateY(-30px);
opacity: 0
}
.page-component {
padding-bottom: 95px;
box-sizing: border-box
}
.page-component .content {
margin-left: -1px
}
.page-component .content > h3 {
margin: 45px 0 15px
}
.page-component .content > table {
border-collapse: collapse;
width: 100%;
background-color: #fff;
color: #5e6d82;
font-size: 14px;
margin-bottom: 45px
}
.page-component .content > table strong {
font-weight: 400
}
.page-component .content > table th {
text-align: left;
border-top: 1px solid #eaeefb;
background-color: #eff2f7;
white-space: nowrap
}
.page-component .content > table td, .page-component .content > table th {
border-bottom: 1px solid #eaeefb;
padding: 10px;
max-width: 250px
}
.page-component .content > table td:first-child, .page-component .content > table th:first-child {
padding-left: 10px
}
.page-component .page-component-up {
background-color: #58b7ff;
position: fixed;
right: 100px;
bottom: 150px;
width: 50px;
height: 50px;
border-radius: 25px;
cursor: pointer;
opacity: .4;
transition: .3s
}
.page-component .page-component-up i {
color: #fff;
display: block;
line-height: 50px;
text-align: center;
font-size: 22px
}
.page-component .page-component-up.hover {
opacity: 1
}
.page-component .back-top-fade-enter, .page-component .back-top-fade-leave-active {
transform: translateY(-30px);
opacity: 0
}
\ No newline at end of file
<template>
<div class='app-body'>
<div class='main-container'>
<router-view></router-view>
</div>
</div>
</template>
<style>
@import 'content.css';
</style>
<script type='babel'>
export default {
name:'v-content',
data(){
return {
}
},
components: {},
mounted() {
}
};
</script>
.footer {
height: 120px;
background-color: #324057;
color: #a4aebd;
width: 100%;
}
.footer * {
word-spacing: 0
}
.footer .container {
height: 100%;
box-sizing: border-box
}
.footer .footer-main {
font-size: 0;
padding-top: 40px;
display: inline-block
}
.footer .footer-main .footer-main-title {
line-height: 1;
font-size: 22px;
margin: 0
}
.footer .footer-main .footer-main-link {
display: inline-block;
margin: 12px 18px 0 0;
line-height: 1;
font-size: 12px;
color: #768193
}
.footer .footer-main .footer-main-link a {
color: #768193;
text-decoration: none
}
.footer .footer-social {
float: right;
line-height: 120px
}
.footer .footer-social .elementdoc {
transition: .3s;
display: inline-block;
line-height: 32px;
text-align: center;
color: #8d99ab;
background-color: transparent;
width: 32px;
height: 32px;
font-size: 32px;
vertical-align: middle
}
.footer .footer-social .elementdoc:hover {
transform: scale(1.2)
}
.footer .footer-social .doc-icon-weixin {
margin-right: 36px
}
.footer .footer-social .doc-icon-weixin:hover {
color: #fff
}
.footer .footer-social .doc-icon-github {
margin-right: 0
}
.footer .footer-social .doc-icon-github:hover {
color: #fff
}
.footer-popover {
padding: 0;
min-width: 120px;
line-height: normal;
box-shadow: 0 0 11px 0 rgba(174, 187, 211, .24)
}
.footer-popover .footer-popover-title {
border-bottom: 1px solid #eaeefb;
height: 30px;
line-height: 30px;
text-align: center;
color: #99a9bf;
background-color: #f8f9fe
}
.footer-popover img {
width: 100px;
height: 100px;
margin: 10px
}
@media (max-width: 768px) {
.footer .footer-social {
display: none
}
}
.footer-nav {
padding: 24px 0;
color: #99a9bf;
font-size: 14px
}
.footer-nav:after {
content: "";
display: block;
clear: both
}
.footer-nav i {
transition: .3s;
color: #d9def1;
vertical-align: baseline
}
.footer-nav-link {
cursor: pointer;
transition: .3s
}
.footer-nav-link:hover, .footer-nav-link:hover i {
color: #20a0ff
}
.footer-nav-left {
float: left;
margin-left: -4px
}
.footer-nav-right {
float: right;
margin-right: -4px
}
\ No newline at end of file
<template>
<footer class='footer'>
<div class='container'>
<div class='footer-main'><p class='footer-main-title'>IBlog</p></div>
<div class='footer-social'><span><div class='el-popover footer-popover' style='width: 120px; display: none;'>
<div class='footer-popover-title'></div>
</div></span></div>
</div>
</footer>
</template>
<style>
@import './footer.css';
</style>
<script type='babel'>
export default{
};
</script>
.admin-header {
width: 100%;
position: fixed;
display: flex;
height: 50px;
background-color: #3c8dbc;
z-index: 10;
}
.admin-header .admin-logo {
height: 50px;
text-align: center;
line-height: 50px;
color: #fff;
background-color: #367fa9;
-webkit-transition: width 0.35s;
transition: width 0.35s;
}
.admin-header .admin-logo .admin-big {
width: 230px;
display: block;
}
.admin-header .admin-logo .admin-min {
width: 64px;
display: block;
}
.admin-header .right {
position: absolute;
right: 0;
}
.admin-header .admin-header-btn {
overflow: hidden;
height: 50px;
display: inline-block;
text-align: center;
line-height: 50px;
cursor: pointer;
padding: 0 14px;
color: #fff;
}
.admin-header .admin-header-btn:hover {
background-color: #367fa9
}
.admin-header .admin-header-btn .el-badge__content {
top: 14px;
right: 7px;
text-align: center;
font-size: 9px;
padding: 0 3px;
background-color: #00a65a;
color: #fff;
border: none;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25em;
}
.admin-menu {
border-right: none;
}
\ No newline at end of file
<template>
<div style="height:100%">
<header class="admin-header">
<div class="admin-logo">
<span v-bind:class="{'admin-big': !collapse, 'admin-min': collapse }">{{ collapse ? site.name : site.description }}</span>
</div>
<span class="admin-header-btn" @click="sidebarToggle"><i class="el-icon-menu"></i></span>
<div class="right">
<span class="admin-header-btn">
<a v-bind:href="$t('lang.href')"><i class="el-icon-message"></i></a>
</span>
<el-dropdown>
<span class="admin-header-btn">
{{$t('lang.text')}}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="switchLang('en')">English</el-dropdown-item>
<el-dropdown-item @click.native="switchLang('cn')">中文</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<span class="admin-header-btn">
<el-badge :value="2" class="badge">
<i class="el-icon-message"></i>
</el-badge>
</span>
<span class="admin-header-btn">
<i class="el-icon-bell"></i>
</span>
<el-dropdown>
<span class="admin-header-btn">
Admin<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item @click.native="logout">退出系统</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</header>
<LeftMenu :collapse="collapse"></LeftMenu>
</div>
</template>
<script type="babel">
import "./header.css";
import LeftMenu from "../menu/index.vue";
export default {
components: {
LeftMenu
},
data() {
return {
collapse: false,
site: {
name: "Egg",
description: "Egg Vue"
}
};
},
computed: {},
methods: {
sidebarToggle(e) {
e.preventDefault();
if (this.collapse) {
document.body.classList.remove("sidebar-hidden");
this.siteName = "Egg";
this.collapse = false;
} else {
document.body.classList.add("sidebar-hidden");
this.collapse = true;
}
},
logout() {
window.location.replace("/login");
},
switchLang(lang) {
window.location.href = `/admin?locale=${lang}`;
}
}
};
</script>
.admin .search {
margin-top: 8px;
margin-bottom: 16px;
}
.admin label {
padding-left: 8px;
padding-right: 8px;
color: #878d99
}
.admin .search-input{
max-width: 200px;
}
.admin .search-button{
margin-left: 16px;
}
.admin .add-button{
float:right;
margin-right: 16px;
}
.admin .long-input {
max-width: 75%;
}
.admin .top16 {
margin-top: 16px;
}
\ No newline at end of file
import Vue from 'vue';
import MainLayout from './main.vue';
import '../../../asset/css/global.css';
import './index.css';
import createLayout from '../layout';
const tpl = '<div class="admin" id="app" data-server-rendered="true"><MainLayout><div slot="main"><slot></slot></div></MainLayout></div>';
export default createLayout('Layout', { MainLayout }, tpl);
<template>
<div class='main'>
<LayoutHeader></LayoutHeader>
<LayoutContent>
<div slot='content'><slot name='main'></slot></div>
</LayoutContent>
</div>
</template>
<style>
.main {
display: flex;
}
.main .el-menu:not(.el-menu--collapse) {
width: 230px;
}
.main .app {
width: 100%;
background-color: #ecf0f5;
}
.main .app-body {
width: 100%;
margin-left: 230px;
-webkit-transition: margin-left 0.35s;
transition: margin-left 0.35s;
}
.main .main-container {
margin-top: 50px;
padding: 10px;
min-height: calc(100vh - 100px);
}
</style>
<script type='babel'>
import LayoutHeader from './header/header';
import LayoutContent from './content/content';
export default {
components: {
LayoutHeader,
LayoutContent
}
};
</script>
const menu = {
home: {
name: 'menu.home',
path: '/',
icon: 'el-icon-menu',
},
content: {
name: 'menu.articlemanage',
icon: 'el-icon-document',
children: {
list: {
name: 'menu.articlequery',
icon: 'el-icon-edit-outline',
path: '/article/list'
},
add: {
name: 'menu.articleadd',
icon: 'el-icon-edit-outline',
path: '/article/add'
}
}
}
};
export default menu;
\ No newline at end of file
<template>
<div class='aside'>
<el-menu
router
background-color='#222d32'
text-color='#fff'
default-active='1-4-1' class='menu' @open='handleOpen' @close='handleClose' :collapse='collapse'>
<template v-for='(menu_v,menu_k) in menu'>
<el-submenu v-if='menu_v.children' :index='menu_k' :key='menu_k'>
<template slot='title'>
<i :class='menu_v.icon'></i>
<span slot='title'>{{$t(menu_v.name)}}</span>
</template>
<el-menu-item v-for='(menuChildren_v,menuChildren_k) in menu_v.children' :key='menuChildren_k'
:index='menuChildren_v.path'>
<i class='is-children'></i>
<span slot='title'>{{ $t(menuChildren_v.name) }}</span>
</el-menu-item>
</el-submenu>
<el-menu-item v-else :index='menu_v.path' :key='menu_k'>
<i :class='menu_v.icon'></i>
<span slot='title'>{{$t(menu_v.name)}}</span>
</el-menu-item>
</template>
</el-menu>
</div>
</template>
<script type='text/babel'>
import menu from './index.js';
export default {
props: ['collapse'],
data(){
return { menu };
},
methods: {
handleOpen(key, keyPath) {
console.log(key, keyPath);
},
handleClose(key, keyPath) {
console.log(key, keyPath);
}
},
}
</script>
<style>
.aside {
position: fixed;
margin-top: 50px;
min-height: calc(100vh - 50px);
z-index: 10;
background-color: #222d32;
height: 100%;
}
</style>
\ No newline at end of file
import Vue from 'vue';
import MainLayout from './main.vue';
import '../../asset/css/global.css';
import createLayout from './layout';
const tpl = '<div id="app" data-server-rendered="true"><slot></slot></div>';
export default createLayout('Layout', { }, tpl);
\ No newline at end of file
.main{
margin-top: -80px;
padding: 80px 0 120px;
box-sizing: border-box;
}
.page-container{
width: 1140px;
padding: 0 30px;
margin: 36 auto;
}
.page-component {
padding-bottom: 95px;
box-sizing: border-box
}
.page-component .content {
margin-left: -1px
}
.page-component .content > h3 {
margin: 45px 0 15px
}
.page-component .content > table {
border-collapse: collapse;
width: 100%;
background-color: #fff;
color: #5e6d82;
font-size: 14px;
margin-bottom: 45px
}
.page-component .content > table strong {
font-weight: 400
}
.page-component .content > table th {
text-align: left;
border-top: 1px solid #eaeefb;
background-color: #eff2f7;
white-space: nowrap
}
.page-component .content > table td, .page-component .content > table th {
border-bottom: 1px solid #eaeefb;
padding: 10px;
max-width: 250px
}
.page-component .content > table td:first-child, .page-component .content > table th:first-child {
padding-left: 10px
}
.page-component .page-component-up {
background-color: #58b7ff;
position: fixed;
right: 100px;
bottom: 150px;
width: 50px;
height: 50px;
border-radius: 25px;
cursor: pointer;
opacity: .4;
transition: .3s
}
.page-component .page-component-up i {
color: #fff;
display: block;
line-height: 50px;
text-align: center;
font-size: 22px
}
.page-component .page-component-up.hover {
opacity: 1
}
.page-component .back-top-fade-enter, .page-component .back-top-fade-leave-active {
transform: translateY(-30px);
opacity: 0
}
.page-component {
padding-bottom: 95px;
box-sizing: border-box
}
.page-component .content {
margin-left: -1px
}
.page-component .content > h3 {
margin: 45px 0 15px
}
.page-component .content > table {
border-collapse: collapse;
width: 100%;
background-color: #fff;
color: #5e6d82;
font-size: 14px;
margin-bottom: 45px
}
.page-component .content > table strong {
font-weight: 400
}
.page-component .content > table th {
text-align: left;
border-top: 1px solid #eaeefb;
background-color: #eff2f7;
white-space: nowrap
}
.page-component .content > table td, .page-component .content > table th {
border-bottom: 1px solid #eaeefb;
padding: 10px;
max-width: 250px
}
.page-component .content > table td:first-child, .page-component .content > table th:first-child {
padding-left: 10px
}
.page-component .page-component-up {
background-color: #58b7ff;
position: fixed;
right: 100px;
bottom: 150px;
width: 50px;
height: 50px;
border-radius: 25px;
cursor: pointer;
opacity: .4;
transition: .3s
}
.page-component .page-component-up i {
color: #fff;
display: block;
line-height: 50px;
text-align: center;
font-size: 22px
}
.page-component .page-component-up.hover {
opacity: 1
}
.page-component .back-top-fade-enter, .page-component .back-top-fade-leave-active {
transform: translateY(-30px);
opacity: 0
}
\ No newline at end of file
<template>
<div>
<slot name='content'></slot>
</div>
</template>
<style>
@import 'content.css';
</style>
<script type='babel'>
export default {
name:'v-content',
data(){
return {
}
},
components: {},
mounted() {
}
};
</script>
<template>
<footer>
<div class='container'>
<div class='row'>
<div class='col-lg-10 col-lg-offset-1 col-md-10 col-md-offset-1'>
<ul class='list-inline text-center'>
<!-- kill the Facebook and Weibo -->
<li>
<a href='/feed.xml'>
<span class='fa-stack fa-lg'>
<i class='fa fa-circle fa-stack-2x'></i>
<i class='fa fa-rss fa-stack-1x fa-inverse'></i>
</span>
</a>
</li>
</ul>
<p class='copyright text-muted'>2018 ❖ Powered by hubcarl.
</p>
</div>
</div>
</div>
</footer>
</template>
<style>
@import './footer.css';
</style>
<script type='babel'>
export default{
};
</script>
.header {
width: 100%;
position: fixed;
display: flex;
height: 50px;
background-color: #3c8dbc;
z-index: 10;
}
.header .logo {
width: 230px;
height: 50px;
text-align: center;
line-height: 50px;
color: #fff;
background-color: #367fa9;
-webkit-transition: width 0.35s;
transition: width 0.35s;
}
.header .logo .min {
display: none;
}
.header .right {
position: absolute;
right: 0;
}
.header .header-btn {
overflow: hidden;
height: 50px;
display: inline-block;
text-align: center;
line-height: 50px;
cursor: pointer;
padding: 0 14px;
color: #fff;
}
.header .header-btn .el-badge__content {
top: 14px;
right: 7px;
text-align: center;
font-size: 9px;
padding: 0 3px;
background-color: #00a65a;
color: #fff;
border: none;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25em;
}
.header .header-btn:hover {
background-color: #367fa9;
}
.menu {
border-right: none;
}
<template>
<div>
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header page-scroll">
<button type="button" @click="menuBtn" class="navbar-toggle" :class="{ 'collapsed': collapsed }" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">IBlog</a>
</div>
<div class="collapse navbar-collapse" :class="{ 'collapse in': collapsed }" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li>
<a href="/">{{$t('menu.home')}}</a>
</li>
<li>
<a href="/category">{{$t('menu.category')}}</a>
</li>
</ul>
</div>
</div>
</nav>
<header class="intro-header" style="background-color:#3c763d;">
<div class="container">
<div class="row">
<div class="col-lg-10 col-lg-offset-1 col-md-10 col-md-offset-1">
<div class="site-heading">
<div>
<img src="../../../../asset/images/loading.gif" width="80px" height="80px" style="display: inline">
</div>
<h1>博客</h1>
<hr class="large">
<span class="subheading">IBlog</span>
</div>
</div>
</div>
</div>
</header>
</div>
</template>
<script type="babel">
import "./header.css";
export default {
components: {},
data() {
return {
collapsed: false
};
},
computed: {},
methods: {
menuBtn() {
this.collapsed = !this.collapsed;
}
},
mounted() {}
};
</script>
import Vue from 'vue';
import MainLayout from './main.vue';
import '../../../asset/css/bootstrap.css';
import '../../../asset/css/blog.css';
import createLayout from '../layout';
export default createLayout('Layout', { MainLayout }, '<div id="app" data-server-rendered="true"><MainLayout><div slot="main"><slot></slot></div></MainLayout></div>');
<template>
<div class='main'>
<LayoutHeader></LayoutHeader>
<LayoutContent>
<div slot='content'><slot name='main'></slot></div>
</LayoutContent>
</div>
</template>
<style>
</style>
<script type='babel'>
import LayoutHeader from './header/header';
import LayoutContent from './content/content';
export default {
components: {
LayoutHeader,
LayoutContent
}
};
</script>
import Vue from 'vue';
export default function createLayout(name, components, tpl) {
return {
name,
props: ['title', 'description', 'keywords'],
components,
computed: {
vTitle() {
return this.$root.title || this.title || 'IBlog';
},
vKeywords() {
return this.$root.keywords || this.keywords || 'egg, typescript, vue, webpack, server side render';
},
vDescription() {
return this.$root.description || this.description || 'IBlog';
},
baseClass() {
return this.$root.baseClass;
}
},
template: EASY_ENV_IS_BROWSER ? tpl : `<!DOCTYPE html>
<html lang="en">
<head>
<title>{{vTitle}}</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="keywords" :content="vKeywords">
<meta name="description" :content="vDescription">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<!-- Add to home screen for Windows -->
<meta name="msapplication-TileImage" content="images/icons/icon-144x144.png">
<meta name="msapplication-TileColor" content="#2F3BA2">
<!-- Add to home screen for Safari on iOS -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Weather PWA">
<link rel="apple-touch-icon" href="/logo.png">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
</head>
<body :class="baseClass">
${tpl}
</body>
</html>`,
install(vue, options) {
//
}
};
}
import Layout from 'component/layout/index';
import plugin from 'framework/plugin';
export default function(Vue) {
Vue.use(plugin);
Vue.component(Layout.name, Layout);
}
\ No newline at end of file
'use strict';
export default {
menu: {
home: '首页',
articlemanage: '文章管理',
articlequery: '文章查询',
articleadd: '添加文章'
},
lang: {
href: '/admin?locale=en',
text: '中文'
}
};
\ No newline at end of file
export default {
menu: {
home: 'Home',
articlemanage: 'Article Manage',
articlequery: 'Article Query',
articleadd: 'Article Add'
},
lang: {
href: '/admin?locale=cn',
text: 'English'
}
};
\ No newline at end of file
import VueI18n from 'vue-i18n';
import cn from './cn';
import en from './en';
export default function createI18n(locale) {
return new VueI18n({
locale,
messages: {
en,
cn
}
});
}
\ No newline at end of file
'use strict';
export default {
menu: {
home: '首页',
category: '分类',
about: '关于'
},
lang: {
href: '/?locale=en',
text: '中文'
}
};
\ No newline at end of file
export default {
menu: {
home: 'Home',
category: 'Catetory',
about: 'About',
},
lang: {
href: '/?locale=cn',
text: 'English'
}
};
\ No newline at end of file
import VueI18n from 'vue-i18n';
import cn from './cn';
import en from './en';
export default function createI18n(locale) {
return new VueI18n({
locale,
messages: {
en,
cn
}
});
}
\ No newline at end of file
'use strict';
import axios from 'axios';
// axios.defaults.baseURL = 'http://127.0.0.1:7001';
axios.defaults.timeout = 15000;
axios.defaults.xsrfHeaderName = 'x-csrf-token';
axios.defaults.xsrfCookieName = 'csrfToken';
export default {
post(url, json, store = {}) {
const { state = { origin: '' } } = store;
const headers = {};
if (EASY_ENV_IS_NODE) {
headers['x-csrf-token'] = state.csrf;
headers.Cookie = `csrfToken=${state.csrf}`;
}
return axios.post(`${state.origin}${url}`, json, { headers });
},
get(url, store = {}) {
const { state = { origin: '' } } = store;
return axios.get(`${state.origin}${url}`);
}
};
\ No newline at end of file
'use strict';
import axios from 'axios';
import request from 'framework/network/request';
import VueI18n from 'vue-i18n';
import createI18n from 'framework/i18n/site';
export default {
install(Vue) {
if (!Vue.prototype.hasOwnProperty('$request')) {
Vue.prototype.$request = request;
}
if (!Vue.hook) {
Vue.use(VueI18n);
Vue.hook = {
render(context, options) {
const i18n = createI18n(context.state.locale);
options.i18n = i18n;
}
};
}
}
};
\ No newline at end of file
<template>
<layout description='vue server side render' keywords='egg, vue, webpack, server side render'>
<div class='container'>
<h2>IBlog: About</h2>
</div>
</layout>
</template>
<style>
@import 'about.css';
</style>
<script type='babel'>
export default {
components: {
},
data(){
return {
}
},
computed: {
},
methods: {
},
mounted() {
}
}
</script>
<template>
<el-row class="panel-group" :gutter="40">
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class='card-panel' @click="handleSetLineChartData('newVisitis')">
<div class="card-panel-icon-wrapper icon-people">
<div icon-class="peoples" class-name="card-panel-icon"></div>
</div>
<div class="card-panel-description">
<div class="card-panel-text">New Visits</div>
<span class="card-panel-num">2600</span>
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('messages')">
<div class="card-panel-icon-wrapper icon-message">
<div icon-class="message" class-name="card-panel-icon" ></div>
</div>
<div class="card-panel-description">
<div class="card-panel-text">Messages</div>
<span class="card-panel-num">3000</span>
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('purchases')">
<div class="card-panel-icon-wrapper icon-money">
<div icon-class="money" class-name="card-panel-icon" ></div>
</div>
<div class="card-panel-description">
<div class="card-panel-text">Purchases</div>
<span class="card-panel-num">3200</span>
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('shoppings')">
<div class="card-panel-icon-wrapper icon-shoppingCard">
<div icon-class="shoppingCard" class-name="card-panel-icon" ></div>
</div>
<div class="card-panel-description">
<div class="card-panel-text">Shoppings</div>
<span class="card-panel-num">3200</span>
</div>
</div>
</el-col>
</el-row>
</template>
<script>
export default {
components: {},
methods: {
handleSetLineChartData(type) {
this.$emit("handleSetLineChartData", type);
}
}
};
</script>
<style scoped>
.panel-group {
margin-top: 18px;
}
.panel-group .card-panel-col {
margin-bottom: 32px;
}
.panel-group .card-panel {
height: 108px;
cursor: pointer;
font-size: 12px;
position: relative;
overflow: hidden;
color: #666;
background: #fff;
box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05);
border-color: rgba(0, 0, 0, 0.05);
}
.panel-group .card-panel:hover .card-panel-icon-wrapper {
color: #fff;
}
.panel-group .card-panel:hover .icon-people {
background: #40c9c6;
}
.panel-group .card-panel:hover .icon-message {
background: #36a3f7;
}
.panel-group .card-panel:hover .icon-money {
background: #f4516c;
}
.panel-group .card-panel:hover .icon-shoppingCard {
background: #34bfa3;
}
.panel-group .card-panel .icon-people {
color: #40c9c6;
}
.panel-group .card-panel .icon-message {
color: #36a3f7;
}
.panel-group .card-panel .icon-money {
color: #f4516c;
}
.panel-group .card-panel .icon-shoppingCard {
color: #34bfa3;
}
.panel-group .card-panel .card-panel-icon-wrapper {
float: left;
margin: 14px 0 0 14px;
padding: 16px;
transition: all 0.38s ease-out;
border-radius: 6px;
}
.panel-group .card-panel .card-panel-icon {
float: left;
font-size: 48px;
}
.panel-group .card-panel .card-panel-description {
float: right;
font-weight: bold;
margin: 26px;
margin-left: 0px;
}
.panel-group .card-panel .card-panel-description .card-panel-text {
line-height: 18px;
color: rgba(0, 0, 0, 0.45);
font-size: 16px;
margin-bottom: 12px;
}
.panel-group .card-panel .card-panel-description .card-panel-num {
font-size: 20px;
}
</style>
<template>
<AdminLayout :title="title">
<transition name="fade" mode="out-in">
<router-view></router-view>
</transition>
</AdminLayout>
</template>
<script type="text/babel">
import 'element-ui/lib/theme-chalk/index.css';
import Vue from 'vue';
import ElementUI from 'element-ui';
import VueI18n from 'vue-i18n';
import createI18n from 'framework/i18n/admin';
import store from './store/app';
import router from './router';
import AdminLayout from 'component/layout/admin';
Vue.use(VueI18n);
Vue.use(ElementUI);
export default {
router,
store,
components: {
AdminLayout,
},
// data() {
// return {
// title: this.$store.state.title
// }
// },
computed: {
title() {
return this.$store.state.title;
}
},
hook :{
render(context, options) {
const i18n = createI18n(context.state.locale);
options.i18n = i18n;
}
},
mounted() {},
};
</script>
import Vue from 'vue';
import VueRouter from 'vue-router';
import Dashboard from '../view/dashboard/index.vue';
import ArticleList from '../view/list.vue';
Vue.use(VueRouter);
export default function createRouter() {
const router = new VueRouter({
mode: 'history',
base: '/admin/',
routes: [
{
path: '/',
component: Dashboard
},
{
path: '/article/list',
component: ArticleList
},
{
path: '/article/add',
component: () => import('../view/write/index.vue')
},
{
path: '/article/detail/:id',
component: () => import('../view/detail.vue')
},
{
path: '*', component: () => import('../view/notfound.vue')
}
]
});
router.beforeEach((route, redirec, next) => {
next();
});
router.afterEach((route, redirec) => {
if (EASY_ENV_IS_BROWSER && route.matched && route.matched.length) {
const matchComponent = route.matched[0].components.default;
const asyncData = matchComponent.methods && matchComponent.methods.fetchApi;
if (asyncData) {
console.log('router afterEach trigger asyncData', route);
asyncData(router.app.$store, route);
}
}
});
return router;
}
\ No newline at end of file
'use strict';
import * as Type from './mutation-type';
import Vue from 'vue';
import Vuex from 'vuex';
import request from 'framework/network/request';
Vue.use(Vuex);
const actions = {
SET_ARTICLE_LIST: (store, json) => {
return request.post('/admin/api/article/list', json, store).then(response => {
store.commit(Type.SET_ARTICLE_LIST, response.data);
});
},
SET_ARTICLE_DETAIL: (store, json) => {
const { commit, dispatch, state } = store;
return request.get(`/admin/api/article/${json.id}`, store)
.then(response => {
commit(Type.SET_ARTICLE_DETAIL, response.data);
});
},
SET_SAVE_ARTICLE: (store, json) => {
const { commit, dispatch, state } = store;
return request.post('/admin/api/article/add', json, store).then(response => {
commit(Type.SET_SAVE_ARTICLE, json);
});
},
DELETE_ARTICLE: (store, { id }) => {
// 鉴权 TODO
const { commit, dispatch, state } = store;
return request.get(`/admin/api/article/del/${id}`, store)
.then(response => {
commit(Type.DELETE_ARTICLE, { id });
});
},
};
export default actions;
\ No newline at end of file
'use strict';
export default {};
\ No newline at end of file
'use strict';
import Vue from 'vue';
import Vuex from 'vuex';
import actions from './actions';
import getters from './getters';
import mutations from './mutations';
Vue.use(Vuex);
export default function createStore(initState) {
const state = {
articleTotal: 0,
articleList: [],
article: {},
...initState
};
return new Vuex.Store({
state,
actions,
getters,
mutations
});
}
\ No newline at end of file
'use strict';
export const SET_ARTICLE_LIST = 'SET_ARTICLE_LIST';
export const SET_ARTICLE_DETAIL = 'SET_ARTICLE_DETAIL';
export const SET_SAVE_ARTICLE = 'SET_SAVE_ARTICLE';
export const DELETE_ARTICLE = 'DELETE_ARTICLE';
\ No newline at end of file
'use strict';
import {
SET_ARTICLE_LIST,
SET_ARTICLE_DETAIL,
SET_SAVE_ARTICLE,
DELETE_ARTICLE
} from './mutation-type';
const mutations = {
[SET_ARTICLE_LIST](state, { list, total }) {
state.articleTotal = total;
state.articleList = list;
},
[SET_ARTICLE_DETAIL](state, data) {
console.log('>>>data', data);
if (Array.isArray(data) && data.length) {
state.article = data[0];
} else {
state.article = data;
}
},
[SET_SAVE_ARTICLE](state, data) {
state.articleTotal += 1;
state.articleList = [data].concat(state.articleList);
},
[DELETE_ARTICLE](state, { id }) {
state.articleTotal -= 1;
state.articleList = state.articleList.filter(item => {
return item.id !== id;
});
}
};
export default mutations;
<template>
<div style="font-size: 24px; text-align: center">
<panel-group></panel-group>
</div>
</template>
<style>
</style>
<script type="text/babel">
import PanelGroup from '../../component/panel.vue'
export default {
components: {
PanelGroup
},
}
</script>
<template>
<div style="font-size: 24px; text-align: center">
<h1>{{article.title}}</h1>
<iframe :src="article.url" frameborder="0" width="100%" height="600"></iframe>
</div>
</template>
<style>
</style>
<script type="text/babel">
import { SET_ARTICLE_DETAIL } from '../store/app/mutation-type';
export default{
computed: {
article() {
return this.$store.state.article;
}
},
methods: {
fetchApi(store, route) {
const id = route.params.id;
return store.dispatch(SET_ARTICLE_DETAIL, { id })
}
},
}
</script>
\ No newline at end of file
<template>
<div>
<div class="search">
<el-row class="clear">
<label> 标题:</label><el-input class="search-input" clearable v-model="q.title" placeholder="关键字"></el-input>
<label> 分类:</label><el-select v-model="q.categoryId" placeholder="分类">
<el-option v-for="item in categories"
:key="item.id"
:label="item.name"
:value="item.categoryId">
</el-option>
</el-select>
<label> 状态:</label><el-select v-model="q.status" placeholder="状态">
<el-option v-for="item in status"
:key="item.id"
:label="item.name"
:value="item.status">
</el-option>
</el-select>
<el-button class="search-button" type="primary" @click="query()">查询</el-button>
<el-button class="add-button" type="success" @click="write()">写文章</el-button>
</el-row>
</div>
<el-table
:data="articleList"
v-loading="loading"
element-loading-text="拼命加载中"
border
@selection-change="batchSelect"
style="width: 100%;">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="title"
label="标题">
<template slot-scope="props">
<router-link :to="'/article/detail/'+ props.row.id">{{props.row.title}}</router-link>
</template>
</el-table-column>
<el-table-column
prop="hits"
label="点赞"
width="100">
</el-table-column>
<el-table-column
prop="status"
label="状态"
width="100">
<template slot-scope="props">
<span v-text="props.row.status == 1 ? '已发布' : '草稿'"></span>
</template>
</el-table-column>
<el-table-column
label="操作"
width="180">
<template slot-scope="props">
<router-link :to="{params: {id: props.row.id}}" tag="span">
<el-button type="info" size="small" icon="edit" @click="handleEdit(props.$index, props.row)">修改</el-button>
</router-link>
<el-button type="danger" size="small" icon="delete" @click="handleDelete(props.$index, props.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 16px">
<div style="float:left">
<el-button
type="danger"
icon="delete"
size="small"
:disabled="batchSelectArray.length === 0"
@click="batchDel"
slot="handler">
<span>批量删除</span>
</el-button>
</div>
<div style="float:right">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="q.pageIndex"
:page-sizes="[10, 15, 20, 50]"
:page-size="q.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</div>
</div>
</template>
<style>
</style>
<script type="babel">
import { SET_ARTICLE_LIST, DELETE_ARTICLE } from '../store/app/mutation-type';
export default {
data() {
return {
q: {
title: undefined,
categoryId: undefined,
statusId: undefined,
pageIndex: 1,
pageSize: 10
},
//请求时的loading效果
loading: false,
//批量选择数组
batchSelectArray: []
};
},
methods: {
fetchApi(store, route, json = {}) {
console.log('>>>route', route);
return store.dispatch(SET_ARTICLE_LIST, json);
},
query() {
this.fetchApi(this.$store, this.$route, this.q);
},
write() {
this.$router.push("/article/add");
},
handleSelectionChange(val) {
console.log("handleSelectionChange", val);
},
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
this.q.pageSize = val;
this.fetchApi(this.$store, this.$route, this.q);
},
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
this.q.pageIndex = val;
this.fetchApi(this.$store, this.$route, this.q);
},
handleEdit(index, row) {
this.$message(`你点击了编辑操作 index:${index}, id:${row.id}`);
},
handleDelete(index, row) {
this.$store.dispatch(DELETE_ARTICLE, { id: row.id });
this.$message(`删除[${row.title}]成功!`);
},
//批量选择
batchSelect(val) {
this.batchSelectArray = val;
},
//批量删除
batchDel() {
this.$confirm("将批量删除选择文章, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.loading = true;
this.$message.success(msg);
this.loading = false;
});
}
},
computed: {
status() {
return [
{ status: undefined, name: "--请选择--" },
{ status: 1, name: "已发布" },
{ status: 2, name: "草稿" }
];
},
categories() {
return [
{ categoryId: 0, name: "--请选择--" },
{ categoryId: 1, name: "Nodejs" },
{ categoryId: 2, name: "Webpack" },
{ categoryId: 3, name: "Egg" }
];
},
total() {
return this.$store.state.articleTotal;
},
articleList() {
return this.$store.state.articleList;
}
}
};
</script>
\ No newline at end of file
<template>
<div style="font-size: 24px; text-align: center">
Not Find The Page!!!
</div>
</template>
<style>
</style>
<script type="babel">
export default {
}
</script>
\ No newline at end of file
<template>
<div>
<div class="search">
<el-row class="clear">
<el-input class="long-input" clearable v-model="article.title" placeholder="文章标题"></el-input>
<el-button class="add-button" type="success" icon="el-icon-document" @click="submit(1)">提交</el-button>
<el-button class="add-button" type="primary" icon="el-icon-document" @click="submit(0)">草稿</el-button>
</el-row>
<el-row class="clear top16">
<el-input class="long-input" clearable v-model="article.tag" placeholder="标签,多个用空格隔开"></el-input>
</el-row>
</div>
<div class="editor-container" v-if="isShowEditor">
<markdown-editor id="contentEditor" ref="contentEditor" v-model="article.content" :height="500" :zIndex="20"></markdown-editor>
</div>
</div>
</template>
<style>
.editor-container {
width: 100%;
}
</style>
<script type="babel">
import { SET_SAVE_ARTICLE } from '../../store/app/mutation-type';
export default {
components: {
MarkdownEditor: () => import("component/MarkdownEditor/index.vue")
},
data() {
return {
article: {
content: "Markdown Write",
html: "",
title: "",
tag: ""
},
isShowEditor: false
};
},
computed: {},
methods: {
markdown2Html() {
import('showdown').then(showdown => {
const converter = new showdown.Converter()
this.article.html = converter.makeHtml(this.content)
})
},
submit(status) {
this.article.status = status;
this.$store.dispatch(SET_SAVE_ARTICLE, this.article);
this.$message(`添加成功`);
}
},
mounted() {
this.isShowEditor = true;
}
};
</script>
\ No newline at end of file
.login {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
height: 100%;
width: 100%;
background-color: #e4e5e6;
}
.login .login-info input {
width: 100%;
}
.login .login-form {
width: 375px;
height: 400px;
padding: 30px;
background-color: white;
text-align: left;
border-radius: 4px;
position: relative;
margin-left: 0;
margin-right: 0;
zoom: 1;
display: block;
}
.login .login-header {
text-align: center;
font-size: 16px;
font-weight: bold;
margin-bottom: 20px;
}
.login .el-checkbox__label {
font-size: 14px;
font-weight: normal;
padding-left: 4px;
}
<template>
<div class="login">
<div class="login-form">
<div class="login-header">
<img src="../../../asset/images/logo.png" height="100" alt="">
<p>IBlog</p>
</div>
<div class="login-info">
<el-input
placeholder="请输入用户名"
suffix-icon="fa fa-user"
v-model="userName"
style="margin-bottom: 18px"
>
</el-input>
<el-input
placeholder="请输入密码"
suffix-icon="fa fa-keyboard-o"
v-model="password"
type="password"
style="margin-bottom: 18px"
>
</el-input>
</div>
<el-button
type="primary"
style="width: 100%;margin-bottom: 18px"
@click.native="login">登录</el-button>
<div>
<el-checkbox class="login-remember" v-model="remenber">记住密码</el-checkbox>
<a href="javascript:;" style="float: right;color: #3C8DBC;font-size: 14px">注册</a>
</div>
</div>
</div>
</template>
<style>
@import "login.css";
</style>
<script type="text/babel">
import Vue from "vue";
import {
Input,
Button,
Checkbox
} from 'element-ui';
Vue.component(Input.name, Input);
Vue.component(Button.name, Button);
Vue.component(Checkbox.name, Checkbox);
export default {
data:{
userName: "",
password: "",
remenber: true
},
methods: {
login() {
window.location.replace("/admin");
}
}
};
</script>
\ No newline at end of file
<template>
<layout description='vue server side render' keywords='egg, vue, webpack, server side render'>
<div class='container'>
<h2>{{ message }}</h2>
<el-button type="primary">element-ui</el-button>
</div>
</layout>
</template>
<style>
@import 'category.css';
</style>
<script type='babel'>
import Vue from 'vue';
import { Button, Select } from 'element-ui';
Vue.use(Button);
export default {
components: {
},
data(){
return {
}
},
computed: {
},
methods: {
},
mounted() {
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('/public/sw-category.js');
}
}
}
</script>
<template>
<layout description="vue server side render" keywords="egg, vue, webpack, server side render">
<div class="container">
<div class="row" v-for="item in lists" :key="item.id">
<div class="col-lg-10 col-lg-offset-1 col-md-10 col-md-offset-1">
<div class="post-preview">
<div :href="item.url">
<h2 class="post-title">
<a :href="item.url" target="_blank" style="font-size: 26px;">{{item.title}}</a>
</h2>
<div class="post-content-preview">{{item.summary}}</div>
</div>
<div class="post-meta">Posted by hubcarl on 17-09-24</div>
</div>
<hr>
</div>
</div>
</div>
<div style="text-align:center" v-if="isLoading">
<img src="../../asset/images/loading.gif">
</div>
</layout>
</template>
<style>
@import "index.css";
</style>
<script type="babel">
export default {
components: {
},
data(){
return {
isFinish: false,
isLoading : false,
pageIndex: 1,
pageSize: 10
}
},
computed: {
lists(){
return this.list;
}
},
methods: {
fetch(){
this.$request.get(`/list?pageIndex=${this.pageIndex}&pageSize=${this.pageSize}`).then(res=> {
console.log('res', res);
if(res.data.list && res.data.list.length){
this.total = res.data.total;
this.list = this.list.concat(res.data.list);
}else{
this.isFinish = true;
}
this.isLoading = false;
});
},
loadPage(){
if (!this.isFinish && !this.isLoading) {
this.isLoading = true;
this.pageIndex++;
setTimeout(()=>{
this.fetch();
}, 1500);
}
}
},
mounted() {
window.addEventListener('scroll', ()=>{
this.loadPage();
}, false);
}
}
</script>
<!DOCTYPE html>
<html lang='en'>
<head>
<title>IBlog</title>
<meta name='keywords'>
<meta name='description'>
<meta http-equiv='content-type' content='text/html;charset=utf-8'>
<meta name='viewport' content='initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui'>
<link rel='shortcut icon' href='/favicon.ico' type='image/x-icon' />
</head>
<body>
<div id='app'><!--vue-ssr-outlet--></div>
</body>
</html>
\ No newline at end of file
module.exports = {
"env": {
"node": {
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"targets": {
"node": "current"
}
}
]
],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
],
"@babel/plugin-syntax-dynamic-import"
]
},
"web": {
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"targets": {
"browsers": [
"last 2 versions",
"safari >= 7"
]
}
}
]
],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
],
"@babel/plugin-proposal-object-rest-spread",
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-transform-object-assign"
]
}
}
}
{
"user": {},
"article": [
{
"id": 1,
"title": "vue-渐进式JavaScript 框架",
"summary": "简单小巧的核心,渐进式技术栈,足以应付任何规模的应用",
"hits": 200,
"url": "https://cn.vuejs.org",
"status": 1,
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348289
},
{
"id": 2,
"title": "Webpack 配置官方文档",
"summary": "webpack is a module bundler for modern JavaScript applications.",
"hits": 550,
"url": "https://webpack.js.org/configuration/",
"status": 1,
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348290
},
{
"id": 3,
"title": "egg-为企业级框架和应用而生",
"summary": "Born to buildbetter enterprise frameworks and apps with Node.js & Koa",
"hits": 278,
"url": "https://eggjs.org/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348290
},
{
"id": 5,
"title": "Centralized State Management for Vue.js",
"summary": "Vuex 是一个专为Vue.js 应用程序开发的状态管理模式",
"hits": 232,
"url": "https://github.com/vuejs/vuex",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348293
},
{
"id": 6,
"title": "vue服务器渲染",
"summary": "服务器渲染可以加快首屏速度,利于SEO",
"hits": 565,
"url": "http://csbun.github.io/blog/2016/08/vue-2-0-server-side-rendering/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348293
},
{
"id": 7,
"title": "webpack服务器构建",
"summary": "Webpack is an amazing tool.",
"hits": 988,
"url": "http://jlongster.com/Backend-Apps-with-Webpack--Part-I",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348294
},
{
"id": 8,
"title": "vue component loader for Webpack",
"summary": "Webpack loader for Vue.js components",
"hits": 322,
"url": "https://github.com/vuejs/vue-loader",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348295
},
{
"id": 9,
"title": "vue-router--The official router for Vue.js",
"summary": "It deeply integrates with Vue.js core to make building Single Page Applications with Vue.js a breeze",
"hits": 566,
"url": "https://github.com/vuejs/vue-router",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348295
},
{
"id": 10,
"title": "vue生命周期",
"summary": "Vue.js 生命周期和route的生命周期讲解",
"hits": 434,
"url": "http://www.jianshu.com/p/e9f884b6ba6c",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348296
},
{
"id": 11,
"title": "babel到底将代码转换成什么鸟样",
"summary": "将babel捧作前端一个划时代的工具一定也不为过,它的出现让许多程序员幸福地用上了es6新语法",
"hits": 432,
"url": "https://github.com/lcxfs1991/blog/issues/9",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348296
},
{
"id": 12,
"title": "HTTP2 的真正性能到底如何",
"summary": "HTTP2 的真正性能到底如何",
"hits": 565,
"url": "https://segmentfault.com/a/1190000007219256?utm_source=weekly&utm_medium=email&utm_campaign=email_weekly",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348296
},
{
"id": 13,
"title": "HTTP,HTTP2.0,SPDY,HTTPS讲解",
"summary": "使用SPDY加快你的网站速度",
"hits": 787,
"url": "http://www.alloyteam.com/2016/07/httphttp2-0spdyhttps-reading-this-is-enough/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348297
},
{
"id": 14,
"title": "git - 简明指南",
"summary": "助你入门 git 的简明指南",
"hits": 121,
"url": "http://rogerdudler.github.io/git-guide/index.zh.html",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348297
},
{
"id": 15,
"title": "vue从1升级到2",
"summary": "Migrating from v1 to v2",
"hits": 555,
"url": "https://webpack.js.org/guides/migrating/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348298
},
{
"id": 16,
"title": "vue-渐进式JavaScript 框架",
"summary": "简单小巧的核心,渐进式技术栈,足以应付任何规模的应用",
"hits": 200,
"url": "https://cn.vuejs.org",
"status": 1,
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348299
},
{
"id": 17,
"title": "webpack配置官方文档",
"summary": "webpack is a module bundler for modern JavaScript applications.",
"hits": 550,
"url": "https://webpack.js.org/configuration/",
"status": 1,
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348321
},
{
"id": 18,
"title": "egg-为企业级框架和应用而生",
"summary": "Born to buildbetter enterprise frameworks and apps with Node.js & Koa",
"hits": 278,
"url": "https://eggjs.org/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348322
},
{
"id": 20,
"title": "Centralized State Management for Vue.js",
"summary": "Vuex 是一个专为Vue.js 应用程序开发的状态管理模式",
"hits": 232,
"url": "https://github.com/vuejs/vuex",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348326
},
{
"id": 21,
"title": "vue服务器渲染",
"summary": "服务器渲染可以加快首屏速度,利于SEO",
"hits": 565,
"url": "http://csbun.github.io/blog/2016/08/vue-2-0-server-side-rendering/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348327
},
{
"id": 22,
"title": "webpack服务器构建",
"summary": "Webpack is an amazing tool.",
"hits": 988,
"url": "http://jlongster.com/Backend-Apps-with-Webpack--Part-I",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348329
},
{
"id": 23,
"title": "vue component loader for Webpack",
"summary": "Webpack loader for Vue.js components",
"hits": 322,
"url": "https://github.com/vuejs/vue-loader",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348330
},
{
"id": 24,
"title": "vue-router--The official router for Vue.js",
"summary": "It deeply integrates with Vue.js core to make building Single Page Applications with Vue.js a breeze",
"hits": 566,
"url": "https://github.com/vuejs/vue-router",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348330
},
{
"id": 25,
"title": "vue生命周期",
"summary": "Vue.js 生命周期和route的生命周期讲解",
"hits": 434,
"url": "http://www.jianshu.com/p/e9f884b6ba6c",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348331
},
{
"id": 26,
"title": "babel到底将代码转换成什么鸟样",
"summary": "将babel捧作前端一个划时代的工具一定也不为过,它的出现让许多程序员幸福地用上了es6新语法",
"hits": 432,
"url": "https://github.com/lcxfs1991/blog/issues/9",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348333
},
{
"id": 27,
"title": "HTTP2 的真正性能到底如何",
"summary": "HTTP2 的真正性能到底如何",
"hits": 565,
"url": "https://segmentfault.com/a/1190000007219256?utm_source=weekly&utm_medium=email&utm_campaign=email_weekly",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348333
},
{
"id": 28,
"title": "HTTP,HTTP2.0,SPDY,HTTPS讲解",
"summary": "使用SPDY加快你的网站速度",
"hits": 787,
"url": "http://www.alloyteam.com/2016/07/httphttp2-0spdyhttps-reading-this-is-enough/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348334
},
{
"id": 29,
"title": "git - 简明指南",
"summary": "助你入门 git 的简明指南",
"hits": 121,
"url": "http://rogerdudler.github.io/git-guide/index.zh.html",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348335
},
{
"id": 30,
"title": "vue从1升级到2",
"summary": "Migrating from v1 to v2",
"hits": 555,
"url": "https://webpack.js.org/guides/migrating/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348335
},
{
"id": 31,
"title": "vue-渐进式JavaScript 框架",
"summary": "简单小巧的核心,渐进式技术栈,足以应付任何规模的应用",
"hits": 200,
"url": "https://cn.vuejs.org",
"status": 1,
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348336
},
{
"id": 32,
"title": "webpack配置官方文档",
"summary": "webpack is a module bundler for modern JavaScript applications.",
"hits": 550,
"url": "https://webpack.js.org/configuration/",
"status": 1,
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348336
},
{
"id": 33,
"title": "egg-为企业级框架和应用而生",
"summary": "Born to buildbetter enterprise frameworks and apps with Node.js & Koa",
"hits": 278,
"url": "https://eggjs.org/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348338
},
{
"id": 35,
"title": "Centralized State Management for Vue.js",
"summary": "Vuex 是一个专为Vue.js 应用程序开发的状态管理模式",
"hits": 232,
"url": "https://github.com/vuejs/vuex",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348339
},
{
"id": 36,
"title": "vue服务器渲染",
"summary": "服务器渲染可以加快首屏速度,利于SEO",
"hits": 565,
"url": "http://csbun.github.io/blog/2016/08/vue-2-0-server-side-rendering/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348340
},
{
"id": 37,
"title": "webpack服务器构建",
"summary": "Webpack is an amazing tool.",
"hits": 988,
"url": "http://jlongster.com/Backend-Apps-with-Webpack--Part-I",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348340
},
{
"id": 38,
"title": "vue component loader for Webpack",
"summary": "Webpack loader for Vue.js components",
"hits": 322,
"url": "https://github.com/vuejs/vue-loader",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348341
},
{
"id": 39,
"title": "vue-router--The official router for Vue.js",
"summary": "It deeply integrates with Vue.js core to make building Single Page Applications with Vue.js a breeze",
"hits": 566,
"url": "https://github.com/vuejs/vue-router",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348341
},
{
"id": 40,
"title": "vue生命周期",
"summary": "Vue.js 生命周期和route的生命周期讲解",
"hits": 434,
"url": "http://www.jianshu.com/p/e9f884b6ba6c",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348342
},
{
"id": 41,
"title": "babel到底将代码转换成什么鸟样",
"summary": "将babel捧作前端一个划时代的工具一定也不为过,它的出现让许多程序员幸福地用上了es6新语法",
"hits": 432,
"url": "https://github.com/lcxfs1991/blog/issues/9",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348342
},
{
"id": 42,
"title": "HTTP2 的真正性能到底如何",
"summary": "HTTP2 的真正性能到底如何",
"hits": 565,
"url": "https://segmentfault.com/a/1190000007219256?utm_source=weekly&utm_medium=email&utm_campaign=email_weekly",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348343
},
{
"id": 43,
"title": "HTTP,HTTP2.0,SPDY,HTTPS讲解",
"summary": "使用SPDY加快你的网站速度",
"hits": 787,
"url": "http://www.alloyteam.com/2016/07/httphttp2-0spdyhttps-reading-this-is-enough/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348343
},
{
"id": 44,
"title": "git - 简明指南",
"summary": "助你入门 git 的简明指南",
"hits": 121,
"url": "http://rogerdudler.github.io/git-guide/index.zh.html",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348344
},
{
"id": 45,
"title": "vue从1升级到2",
"summary": "Migrating from v1 to v2",
"hits": 555,
"url": "https://webpack.js.org/guides/migrating/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348345
},
{
"id": 46,
"title": "vue-渐进式JavaScript 框架",
"summary": "简单小巧的核心,渐进式技术栈,足以应付任何规模的应用",
"hits": 200,
"url": "https://cn.vuejs.org",
"status": 1,
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348345
},
{
"id": 47,
"title": "webpack配置官方文档",
"summary": "webpack is a module bundler for modern JavaScript applications.",
"hits": 550,
"url": "https://webpack.js.org/configuration/",
"status": 1,
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348346
},
{
"id": 48,
"title": "egg-为企业级框架和应用而生",
"summary": "Born to buildbetter enterprise frameworks and apps with Node.js & Koa",
"hits": 278,
"url": "https://eggjs.org/",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348346
},
{
"id": 50,
"title": "Centralized State Management for Vue.js",
"summary": "Vuex 是一个专为Vue.js 应用程序开发的状态管理模式",
"hits": 232,
"url": "https://github.com/vuejs/vuex",
"tag": "egg,vue,webpack",
"categoryId": 1,
"authorId": 1,
"createTime": 1515671348347
}
]
}
\ No newline at end of file
'use strict';
const path = require('path');
const fs = require('fs');
module.exports = app => {
const exports = {};
exports.siteFile = {
'/favicon.ico': fs.readFileSync(path.join(app.baseDir, 'app/web/asset/images/favicon.ico'))
};
exports.vuessr = {
layout: path.join(app.baseDir, 'app/web/view/layout.html'),
renderOptions: {
basedir: path.join(app.baseDir, 'app/view')
},
injectRes:[
{
url: 'https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.0.2/css/swiper.min.css'
},
{
url: 'https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.0.2/js/swiper.min.js'
}
]
};
exports.logger = {
consoleLevel: 'DEBUG',
dir: path.join(app.baseDir, 'logs')
};
exports.static = {
prefix: '/public/',
dir: path.join(app.baseDir, 'public')
};
exports.keys = '123456';
exports.middleware = [
'locals',
'access'
];
exports.security = {
csrf: {
ignoreJSON: false,
cookieName: 'csrfToken',
sessionName: 'csrfToken',
headerName: 'x-csrf-token'
},
xframe: {
enable: false,
},
};
return exports;
};
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册