├── app ├── view │ ├── .gitkeep │ └── README.md ├── web │ ├── page │ │ ├── about │ │ │ ├── about.css │ │ │ └── about.vue │ │ ├── index │ │ │ ├── index.css │ │ │ └── index.vue │ │ ├── category │ │ │ ├── category.css │ │ │ └── category.vue │ │ └── admin │ │ │ ├── home │ │ │ ├── store │ │ │ │ └── app │ │ │ │ │ ├── getters.ts │ │ │ │ │ ├── mutation-type.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── mutations.ts │ │ │ │ │ └── actions.ts │ │ │ ├── view │ │ │ │ ├── notfound.vue │ │ │ │ ├── dashboard │ │ │ │ │ └── index.vue │ │ │ │ ├── detail.vue │ │ │ │ ├── write │ │ │ │ │ └── index.vue │ │ │ │ └── list.vue │ │ │ ├── home.vue │ │ │ ├── home.ts │ │ │ ├── router │ │ │ │ └── index.ts │ │ │ └── component │ │ │ │ └── panel.vue │ │ │ └── login │ │ │ ├── login.ts │ │ │ ├── login.less │ │ │ └── login.vue │ ├── theme │ │ ├── form-item.css │ │ ├── menu-item.css │ │ ├── submenu.css │ │ ├── tab-pane.css │ │ ├── breadcrumb-item.css │ │ ├── button-group.css │ │ ├── checkbox-button.css │ │ ├── checkbox-group.css │ │ ├── collapse-item.css │ │ ├── dropdown-item.css │ │ ├── dropdown-menu.css │ │ ├── menu-item-group.css │ │ ├── aside.css │ │ ├── footer.css │ │ ├── header.css │ │ ├── radio-group.css │ │ ├── fonts │ │ │ ├── element-icons.ttf │ │ │ └── element-icons.woff │ │ ├── main.css │ │ ├── steps.css │ │ ├── card.css │ │ ├── container.css │ │ ├── option-group.css │ │ ├── rate.css │ │ ├── badge.css │ │ ├── option.css │ │ ├── reset.css │ │ ├── row.css │ │ ├── spinner.css │ │ ├── breadcrumb.css │ │ ├── display.css │ │ ├── scrollbar.css │ │ ├── carousel-item.css │ │ ├── collapse.css │ │ ├── alert.css │ │ ├── tree.css │ │ ├── popper.css │ │ ├── switch.css │ │ ├── loading.css │ │ ├── notification.css │ │ ├── message.css │ │ ├── progress.css │ │ ├── popover.css │ │ ├── tag.css │ │ ├── carousel.css │ │ ├── radio-button.css │ │ ├── tooltip.css │ │ ├── dialog.css │ │ ├── form.css │ │ ├── select-dropdown.css │ │ ├── radio.css │ │ ├── icon.css │ │ ├── menu.css │ │ ├── step.css │ │ ├── input.css │ │ ├── color-picker.css │ │ ├── checkbox.css │ │ ├── base.css │ │ └── input-number.css │ ├── typescript │ │ ├── window.d.ts │ │ ├── vue.d.ts │ │ └── webpack.d.ts │ ├── framework │ │ └── vue │ │ │ ├── component │ │ │ └── index.ts │ │ │ ├── directive │ │ │ └── index.ts │ │ │ ├── entry │ │ │ ├── client-loader.ts │ │ │ ├── server-loader.ts │ │ │ ├── client.ts │ │ │ └── server.ts │ │ │ └── filter │ │ │ └── index.ts │ ├── component │ │ ├── layout │ │ │ ├── index │ │ │ │ ├── footer │ │ │ │ │ ├── footer.css │ │ │ │ │ └── footer.vue │ │ │ │ ├── index.ts │ │ │ │ ├── content │ │ │ │ │ ├── content.vue │ │ │ │ │ └── content.css │ │ │ │ ├── main.vue │ │ │ │ └── header │ │ │ │ │ ├── header.less │ │ │ │ │ └── header.vue │ │ │ ├── default.ts │ │ │ ├── admin │ │ │ │ ├── index.ts │ │ │ │ ├── content │ │ │ │ │ ├── content.vue │ │ │ │ │ └── content.css │ │ │ │ ├── index.css │ │ │ │ ├── footer │ │ │ │ │ ├── footer.vue │ │ │ │ │ └── footer.css │ │ │ │ ├── menu │ │ │ │ │ └── index.ts │ │ │ │ ├── main.vue │ │ │ │ ├── header │ │ │ │ │ ├── header.less │ │ │ │ │ └── header.vue │ │ │ │ └── aside │ │ │ │ │ └── aside.vue │ │ │ └── layout.ts │ │ └── MarkdownEditor │ │ │ └── index.vue │ ├── asset │ │ ├── images │ │ │ ├── logo.png │ │ │ ├── favicon.ico │ │ │ ├── loading.gif │ │ │ └── egg-vue-webpack-dev.png │ │ ├── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── fontawesome-webfont.woff2 │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ └── css │ │ │ └── global.css │ ├── tsconfig.json │ └── view │ │ ├── layout.html │ │ └── layout.ejs ├── extend │ ├── context.ts │ └── application.ts ├── model │ ├── artilcedetail.ts │ ├── user.ts │ └── article.ts ├── lib │ ├── db │ │ ├── mongo.ts │ │ ├── msyql.ts │ │ ├── factory.ts │ │ ├── base.ts │ │ ├── collection.ts │ │ └── file.ts │ └── condition.ts ├── controller │ ├── about │ │ └── about.ts │ ├── category │ │ └── category.ts │ ├── admin │ │ └── admin.ts │ └── index │ │ └── index.ts ├── router.ts ├── index.td.ts ├── middleware │ └── access.ts ├── service │ └── article.ts └── mocks │ └── article │ └── list.ts ├── docs ├── TypeScript.md ├── images │ ├── admin.png │ ├── home.png │ ├── webpack.png │ ├── egg-webpack.png │ ├── webpack-build.png │ ├── vue-mutil-page.png │ └── vue-single-page.png ├── perform.md └── issue_template.md ├── config ├── plugin.ts ├── config.test.ts ├── plugin.local.ts ├── config.prod.ts ├── config.local.ts ├── config.default.ts └── tsconfig.json ├── .gitattributes ├── postcss.config.js ├── typings ├── config │ ├── plugin.d.ts │ └── index.d.ts └── app │ ├── extend │ ├── context.d.ts │ └── application.d.ts │ ├── service │ └── index.d.ts │ ├── middleware │ └── index.d.ts │ ├── model │ └── index.d.ts │ └── controller │ └── index.d.ts ├── .babelrc ├── .gitignore ├── test └── lowdb.test.js ├── tslint.json ├── .vscode └── settings.json ├── tsconfig.json ├── webpack.config.js ├── LICENSE ├── README.md └── package.json /app/view/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/TypeScript.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/page/about/about.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/page/index/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/form-item.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/menu-item.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/submenu.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/tab-pane.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/breadcrumb-item.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/button-group.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/checkbox-button.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/checkbox-group.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/collapse-item.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/dropdown-item.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/dropdown-menu.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/theme/menu-item-group.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/typescript/window.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/page/category/category.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/framework/vue/component/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/framework/vue/directive/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/web/component/layout/index/footer/footer.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/view/README.md: -------------------------------------------------------------------------------- 1 | ## egg规范view目录, 保证view文件夹存在, 否则app.config.view.root为空, 编译服务器文件会存放到该目录. -------------------------------------------------------------------------------- /app/web/page/admin/home/store/app/getters.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default {}; -------------------------------------------------------------------------------- /app/web/theme/aside.css: -------------------------------------------------------------------------------- 1 | .el-aside{overflow:auto;-webkit-box-sizing:border-box;box-sizing:border-box} -------------------------------------------------------------------------------- /app/web/theme/footer.css: -------------------------------------------------------------------------------- 1 | .el-footer{padding:0 20px;-webkit-box-sizing:border-box;box-sizing:border-box} -------------------------------------------------------------------------------- /app/web/theme/header.css: -------------------------------------------------------------------------------- 1 | .el-header{padding:0 20px;-webkit-box-sizing:border-box;box-sizing:border-box} -------------------------------------------------------------------------------- /config/plugin.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | vuessr: { 3 | package: 'egg-view-vue-ssr' 4 | } 5 | }; -------------------------------------------------------------------------------- /app/web/typescript/vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from 'vue' 3 | export default Vue 4 | } -------------------------------------------------------------------------------- /docs/images/admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/docs/images/admin.png -------------------------------------------------------------------------------- /docs/images/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/docs/images/home.png -------------------------------------------------------------------------------- /app/extend/context.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | export default { 3 | get db() { 4 | return this.app.db; 5 | } 6 | }; -------------------------------------------------------------------------------- /app/web/theme/radio-group.css: -------------------------------------------------------------------------------- 1 | .el-radio-group{display:inline-block;line-height:1;vertical-align:middle;font-size:0} -------------------------------------------------------------------------------- /docs/images/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/docs/images/webpack.png -------------------------------------------------------------------------------- /app/model/artilcedetail.ts: -------------------------------------------------------------------------------- 1 | export default class ArticleDetail { 2 | public id: number; 3 | public content: string; 4 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=javascript 2 | *.css linguist-language=javascript 3 | *.html linguist-language=javascript -------------------------------------------------------------------------------- /app/web/asset/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/images/logo.png -------------------------------------------------------------------------------- /docs/images/egg-webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/docs/images/egg-webpack.png -------------------------------------------------------------------------------- /docs/images/webpack-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/docs/images/webpack-build.png -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | plugins: [ 5 | require('autoprefixer') 6 | ] 7 | }; 8 | -------------------------------------------------------------------------------- /docs/images/vue-mutil-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/docs/images/vue-mutil-page.png -------------------------------------------------------------------------------- /docs/images/vue-single-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/docs/images/vue-single-page.png -------------------------------------------------------------------------------- /app/web/asset/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/images/favicon.ico -------------------------------------------------------------------------------- /app/web/asset/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/images/loading.gif -------------------------------------------------------------------------------- /app/web/asset/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /app/web/theme/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/theme/fonts/element-icons.ttf -------------------------------------------------------------------------------- /app/web/theme/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/theme/fonts/element-icons.woff -------------------------------------------------------------------------------- /app/web/theme/main.css: -------------------------------------------------------------------------------- 1 | .el-main{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow:auto;-webkit-box-sizing:border-box;box-sizing:border-box;padding:20px} -------------------------------------------------------------------------------- /app/web/asset/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /app/web/asset/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /app/web/asset/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /app/web/asset/images/egg-vue-webpack-dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/images/egg-vue-webpack-dev.png -------------------------------------------------------------------------------- /app/model/user.ts: -------------------------------------------------------------------------------- 1 | export default class User { 2 | public id: number; 3 | public name: string; 4 | public password: string; 5 | public roleId: number; 6 | } -------------------------------------------------------------------------------- /app/web/asset/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /app/web/asset/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /app/web/asset/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /app/web/asset/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /app/web/asset/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubcarl/egg-typescript-element-kit/HEAD/app/web/asset/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /app/lib/db/mongo.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import BaseDB from './base'; 3 | export default class MongoDB extends BaseDB { 4 | constructor(name: string) { 5 | super(name); 6 | } 7 | } -------------------------------------------------------------------------------- /app/lib/db/msyql.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import BaseDB from './base'; 3 | export default class MySQLDB extends BaseDB { 4 | constructor(name?: string) { 5 | super(name); 6 | } 7 | } -------------------------------------------------------------------------------- /config/config.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Application, EggAppConfig } from 'egg'; 3 | 4 | export default (app: EggAppConfig) => { 5 | const exports: any = {}; 6 | 7 | return exports; 8 | }; 9 | -------------------------------------------------------------------------------- /app/web/typescript/webpack.d.ts: -------------------------------------------------------------------------------- 1 | declare var EASY_ENV_IS_NODE: boolean; 2 | declare var EASY_ENV_IS_BROWSER: boolean; 3 | declare var process : { 4 | env: { 5 | NODE_ENV: string 6 | } 7 | } -------------------------------------------------------------------------------- /docs/perform.md: -------------------------------------------------------------------------------- 1 | http://www.jianshu.com/p/1dffe3126686 2 | 3 | http://alexkuz.github.io/webpack-chart/ 4 | http://webpack.github.io/analyse/ 5 | 6 | 7 | http://kiwenlau.com/2017/04/01/nodejs-async-await/ -------------------------------------------------------------------------------- /config/plugin.local.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | cors: { 3 | package: 'egg-cors' 4 | }, 5 | webpack: { 6 | package: 'egg-webpack' 7 | }, 8 | webpackvue : { 9 | package: 'egg-webpack-vue' 10 | } 11 | }; -------------------------------------------------------------------------------- /typings/config/plugin.d.ts: -------------------------------------------------------------------------------- 1 | // This file was auto created by egg-ts-helper 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import 'egg-cors'; 5 | import 'egg-webpack'; 6 | import 'egg-webpack-vue'; 7 | import 'egg-view-vue-ssr'; -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["env",{ "modules": false }]], 3 | "plugins": [ 4 | "transform-object-rest-spread", 5 | "syntax-dynamic-import", 6 | "transform-object-assign" 7 | ], 8 | "comments": false 9 | } 10 | -------------------------------------------------------------------------------- /app/web/page/admin/home/store/app/mutation-type.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export const SET_ARTICLE_LIST = 'SET_ARTICLE_LIST'; 4 | export const SET_ARTICLE_DETAIL = 'SET_ARTICLE_DETAIL'; 5 | export const SET_SAVE_ARTICLE = 'SET_SAVE_ARTICLE'; -------------------------------------------------------------------------------- /app/controller/about/about.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import { Controller } from 'egg'; 3 | export default class AboutController extends Controller { 4 | public async index() { 5 | await this.ctx.render('about/about.js', { message: 'vue server side render!' }); 6 | } 7 | } -------------------------------------------------------------------------------- /app/web/page/admin/home/view/notfound.vue: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /config/config.prod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 生产环境配置 3 | * 4 | * 最终生效的配置为 prod + default(前者覆盖后者) 5 | */ 6 | 7 | import { Application, EggAppConfig } from 'egg'; 8 | 9 | export default (app: EggAppConfig) => { 10 | const exports: any = {}; 11 | 12 | return exports; 13 | }; 14 | -------------------------------------------------------------------------------- /app/web/component/layout/default.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import MainLayout from './main.vue'; 3 | import '../../asset/css/global.css'; 4 | import createLayout from './layout'; 5 | const tpl = '
'; 6 | export default createLayout('Layout', { }, tpl); -------------------------------------------------------------------------------- /typings/app/extend/context.d.ts: -------------------------------------------------------------------------------- 1 | // This file was auto created by egg-ts-helper 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import ExtendObject from '../../../app/extend/context'; 5 | declare module 'egg' { 6 | interface Context { 7 | db: typeof ExtendObject.db; 8 | } 9 | } -------------------------------------------------------------------------------- /app/controller/category/category.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import { Controller } from 'egg'; 3 | export default class CategoryController extends Controller { 4 | public async index() { 5 | await this.ctx.render('category/category.js', { message: 'Egg + TypeScript: Category'}); 6 | } 7 | } -------------------------------------------------------------------------------- /typings/app/service/index.d.ts: -------------------------------------------------------------------------------- 1 | // This file was auto created by egg-ts-helper 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import Article from '../../../app/service/article'; 5 | 6 | declare module 'egg' { 7 | interface IService { 8 | article: Article; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /typings/app/extend/application.d.ts: -------------------------------------------------------------------------------- 1 | // This file was auto created by egg-ts-helper 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import ExtendObject from '../../../app/extend/application'; 5 | declare module 'egg' { 6 | interface Application { 7 | db: typeof ExtendObject.db; 8 | } 9 | } -------------------------------------------------------------------------------- /typings/app/middleware/index.d.ts: -------------------------------------------------------------------------------- 1 | // This file was auto created by egg-ts-helper 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import Access from '../../../app/middleware/access'; 5 | 6 | declare module 'egg' { 7 | interface IMiddleware { 8 | access: typeof Access; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /docs/issue_template.md: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /app/web/theme/steps.css: -------------------------------------------------------------------------------- 1 | .el-steps{display:-webkit-box;display:-ms-flexbox;display:flex}.el-steps--simple{padding:13px 8%;border-radius:4px;background:#f5f7fa}.el-steps--horizontal{white-space:nowrap}.el-steps--vertical{height:100%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-flow:column;flex-flow:column} -------------------------------------------------------------------------------- /app/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../config/tsconfig.json", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "module": "esnext", 6 | "lib": [ 7 | "es6", 8 | "dom" 9 | ] 10 | }, 11 | 12 | "exclude": [ 13 | "node_modules", 14 | "**/*.spec.ts" 15 | ] 16 | } -------------------------------------------------------------------------------- /app/extend/application.ts: -------------------------------------------------------------------------------- 1 | import DB from '../lib/db/base'; 2 | import DBFactory from '../lib/db/factory'; 3 | const DBSymbol = Symbol('Application#db'); 4 | export default { 5 | get db(): DB { 6 | if (!this[DBSymbol]) { 7 | this[DBSymbol] = DBFactory(); 8 | } 9 | return this[DBSymbol]; 10 | }, 11 | }; -------------------------------------------------------------------------------- /app/web/page/admin/home/home.vue: -------------------------------------------------------------------------------- 1 | 8 | 18 | -------------------------------------------------------------------------------- /app/web/theme/card.css: -------------------------------------------------------------------------------- 1 | .el-card{border-radius:4px;border:1px solid #e6ebf5;background-color:#fff;overflow:hidden;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);color:#2d2f33}.el-card__header{padding:18px 20px;border-bottom:1px solid #e6ebf5;-webkit-box-sizing:border-box;box-sizing:border-box}.el-card__body{padding:20px} -------------------------------------------------------------------------------- /app/lib/db/factory.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import FileDB from './file'; 3 | import MongoDB from './mongo'; 4 | import MySQLDB from './msyql'; 5 | export default function(type?: string) { 6 | switch (type) { 7 | case 'mysql': 8 | return new MySQLDB(); 9 | case 'mongo': 10 | return new MySQLDB(); 11 | default: 12 | return new FileDB(); 13 | } 14 | } -------------------------------------------------------------------------------- /app/web/component/layout/admin/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import MainLayout from './main.vue'; 3 | import '../../../asset/css/global.css'; 4 | import './index.css'; 5 | import createLayout from '../layout'; 6 | const tpl = '
'; 7 | export default createLayout('Layout', { MainLayout }, tpl); 8 | -------------------------------------------------------------------------------- /app/web/component/layout/index/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import MainLayout from './main.vue'; 3 | import '../../../asset/css/bootstrap.css'; 4 | import '../../../asset/css/blog.css'; 5 | import createLayout from '../layout'; 6 | const tpl = '
'; 7 | export default createLayout('Layout', { MainLayout }, tpl); 8 | -------------------------------------------------------------------------------- /app/web/theme/container.css: -------------------------------------------------------------------------------- 1 | .el-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-flex:1;-ms-flex:1;flex:1;-webkit-box-sizing:border-box;box-sizing:border-box}.el-container.is-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .happypack/ 3 | node_modules/ 4 | npm-debug.log 5 | .idea/ 6 | dist 7 | static 8 | public 9 | private 10 | run 11 | *.iml 12 | artifacts.json 13 | *tmp 14 | _site 15 | logs 16 | app/**/*.js 17 | config/**/*.js 18 | index.js 19 | config/manifest.json 20 | app/view/* 21 | !app/view/layout.html 22 | !app/view/README.md 23 | !app/view/.gitkeep 24 | package-lock.json 25 | yarn.lock 26 | *.log 27 | coverage -------------------------------------------------------------------------------- /app/web/component/layout/index/content/content.vue: -------------------------------------------------------------------------------- 1 | 6 | 9 | 23 | -------------------------------------------------------------------------------- /app/web/framework/vue/entry/client-loader.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = function(source) { 3 | this.cacheable(); 4 | return ` 5 | import Vue from 'vue'; 6 | import Layout from 'component/layout/index'; 7 | import render from 'client'; 8 | import Page from '${this.resourcePath.replace(/\\/g, '\\\\')}'; 9 | Vue.component(Layout.name, Layout); 10 | export default render({ ...Page }); 11 | `; 12 | }; -------------------------------------------------------------------------------- /app/web/framework/vue/entry/server-loader.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = function(source) { 3 | this.cacheable(); 4 | return ` 5 | import Vue from 'vue'; 6 | import Layout from 'component/layout/index'; 7 | import render from 'server'; 8 | import Page from '${this.resourcePath.replace(/\\/g, '\\\\')}'; 9 | Vue.component(Layout.name, Layout); 10 | export default render({ ...Page }); 11 | `; 12 | }; -------------------------------------------------------------------------------- /app/web/page/admin/home/view/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 10 | 18 | -------------------------------------------------------------------------------- /app/web/page/admin/home/store/app/index.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import Vue from 'vue'; 3 | import Vuex from 'vuex'; 4 | 5 | import actions from './actions'; 6 | import getters from './getters'; 7 | import mutations from './mutations'; 8 | 9 | Vue.use(Vuex); 10 | 11 | const state = { 12 | articleTotal: 0, 13 | articleList: [], 14 | article: {} 15 | }; 16 | 17 | export default new Vuex.Store({ 18 | state, 19 | actions, 20 | getters, 21 | mutations 22 | }); -------------------------------------------------------------------------------- /app/web/framework/vue/filter/index.ts: -------------------------------------------------------------------------------- 1 | // import Vue from 'vue'; 2 | 3 | // /** 4 | // * 去掉HTML标签 5 | // */ 6 | // Vue.filter('removeHtml', input => { 7 | // return input && input.replace(/<(?:.|\n)*?>/gm, '') 8 | // .replace(/(”)/g, '\"') 9 | // .replace(/“/g, '\"') 10 | // .replace(/—/g, '-') 11 | // .replace(/ /g, '') 12 | // .replace(/>/g, '>') 13 | // .replace(/</g, '<') 14 | // .replace(/<[\w\s"':=\/]*/, ''); 15 | // }); -------------------------------------------------------------------------------- /test/lowdb.test.js: -------------------------------------------------------------------------------- 1 | const lowdb = require('lowdb'); 2 | const FileSync = require('lowdb/adapters/FileSync'); 3 | const file = new FileSync('blog.json'); 4 | const db = lowdb(file); 5 | db._.mixin({ 6 | like(array, predicate){ 7 | Object.keys(predicate).forEach(item => { 8 | 9 | }); 10 | } 11 | }) 12 | const result = db.get('article') 13 | .filter(item => { 14 | return item.title && item.title.indexOf('webpack')>-1; 15 | }) 16 | .value(); 17 | 18 | console.log(result); -------------------------------------------------------------------------------- /app/web/theme/option-group.css: -------------------------------------------------------------------------------- 1 | .el-select-group{margin:0;padding:0}.el-select-group__wrap{position:relative;list-style:none;margin:0;padding:0}.el-select-group__wrap:not(:last-of-type){padding-bottom:24px}.el-select-group__wrap:not(:last-of-type)::after{content:'';position:absolute;display:block;left:20px;right:20px;bottom:12px;height:1px;background:#dfe4ed}.el-select-group__title{padding-left:20px;font-size:12px;color:#878d99;line-height:30px}.el-select-group .el-select-dropdown__item{padding-left:20px} -------------------------------------------------------------------------------- /app/web/component/layout/admin/content/content.vue: -------------------------------------------------------------------------------- 1 | 8 | 11 | 25 | -------------------------------------------------------------------------------- /typings/app/model/index.d.ts: -------------------------------------------------------------------------------- 1 | // This file was auto created by egg-ts-helper 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import Article from '../../../app/model/article'; 5 | import Artilcedetail from '../../../app/model/artilcedetail'; 6 | import User from '../../../app/model/user'; 7 | 8 | declare module 'sequelize' { 9 | interface Sequelize { 10 | Article: ReturnType; 11 | Artilcedetail: ReturnType; 12 | User: ReturnType; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/web/component/layout/admin/index.css: -------------------------------------------------------------------------------- 1 | .admin .search { 2 | margin-top: 8px; 3 | margin-bottom: 16px; 4 | } 5 | .admin label { 6 | padding-left: 8px; 7 | padding-right: 8px; 8 | color: #878d99 9 | } 10 | 11 | .admin .search-input{ 12 | max-width: 200px; 13 | } 14 | .admin .search-button{ 15 | margin-left: 16px; 16 | } 17 | .admin .add-button{ 18 | float:right; 19 | margin-right: 16px; 20 | } 21 | 22 | .admin .long-input { 23 | max-width: 75%; 24 | } 25 | 26 | .admin .top16 { 27 | margin-top: 16px; 28 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "ordered-imports": false, 9 | "trailing-comma": false, 10 | "quotemark": [true, "single", "jsx-double"], 11 | "eofline": false, 12 | "object-literal-sort-keys": false, 13 | "interface-name": false, 14 | "arrow-parens": false, 15 | "no-console": false, 16 | "max-line-length": false, 17 | "only-arrow-functions": false 18 | }, 19 | "rulesDirectory": ["app"] 20 | } -------------------------------------------------------------------------------- /app/web/component/layout/index/main.vue: -------------------------------------------------------------------------------- 1 | 9 | 11 | 21 | -------------------------------------------------------------------------------- /app/web/theme/rate.css: -------------------------------------------------------------------------------- 1 | .el-rate__icon,.el-rate__item{position:relative;display:inline-block}.el-rate{height:20px;line-height:1}.el-rate:active,.el-rate:focus{outline-width:0}.el-rate__item{font-size:0;vertical-align:middle}.el-rate__icon{font-size:18px;margin-right:6px;color:#b4bccc;-webkit-transition:.3s;transition:.3s}.el-rate__decimal,.el-rate__icon .path2{position:absolute;top:0;left:0}.el-rate__icon.hover{-webkit-transform:scale(1.15);transform:scale(1.15)}.el-rate__decimal{display:inline-block;overflow:hidden}.el-rate__text{font-size:14px;vertical-align:middle} -------------------------------------------------------------------------------- /app/web/page/admin/login/login.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import ElementUI from 'element-ui'; 3 | import clientRender from 'client'; 4 | import serverRender from 'server'; 5 | import Layout from 'component/layout/default'; 6 | import login from './login.vue'; 7 | import '../../../theme/index.css'; 8 | import '../../../asset/css/font-awesome.min.css'; 9 | 10 | Vue.use(ElementUI); 11 | Vue.component(Layout.name, Layout); 12 | const options = { 13 | ...login, 14 | }; 15 | 16 | export default EASY_ENV_IS_NODE ? serverRender(options) : clientRender(options); 17 | -------------------------------------------------------------------------------- /app/web/theme/badge.css: -------------------------------------------------------------------------------- 1 | .el-badge{position:relative;vertical-align:middle;display:inline-block}.el-badge__content{background-color:#fa5555;border-radius:10px;color:#fff;display:inline-block;font-size:12px;height:18px;line-height:18px;padding:0 6px;text-align:center;white-space:nowrap;border:1px solid #fff}.el-badge__content.is-fixed{position:absolute;top:0;right:10px;-webkit-transform:translateY(-50%) translateX(100%);transform:translateY(-50%) translateX(100%)}.el-badge__content.is-fixed.is-dot{right:5px}.el-badge__content.is-dot{height:8px;width:8px;padding:0;right:0;border-radius:50%} -------------------------------------------------------------------------------- /app/web/asset/css/global.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | outline: none; 5 | box-sizing: border-box; 6 | } 7 | html, body{ 8 | height: 100%; 9 | } 10 | 11 | a { 12 | color: #3c8dbc; 13 | text-decoration: none; 14 | } 15 | 16 | -webkit-scrollbar { 17 | width: 4px; 18 | background-color: #F5F5F5; 19 | } 20 | 21 | -webkit-scrollbar-track { 22 | box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 23 | background-color: #F5F5F5; 24 | } 25 | 26 | -webkit-scrollbar-thumb { 27 | box-shadow: inset 0 0 6px rgba(0, 0, 0, .3); 28 | background-color: #3e8dbb; 29 | } -------------------------------------------------------------------------------- /app/web/theme/option.css: -------------------------------------------------------------------------------- 1 | .el-select-dropdown__item{font-size:14px;padding:0 20px;position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#5a5e66;height:34px;line-height:34px;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:pointer}.el-select-dropdown__item.is-disabled{color:#b4bccc;cursor:not-allowed}.el-select-dropdown__item.is-disabled:hover{background-color:#fff}.el-select-dropdown__item.hover,.el-select-dropdown__item:hover{background-color:#f5f7fa}.el-select-dropdown__item.selected{color:#3C8DBC;font-weight:700}.el-select-dropdown__item span{line-height:34px!important} -------------------------------------------------------------------------------- /app/web/page/admin/home/store/app/mutations.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { 4 | SET_ARTICLE_LIST, 5 | SET_ARTICLE_DETAIL, 6 | SET_SAVE_ARTICLE 7 | } from './mutation-type'; 8 | 9 | const mutations = { 10 | [SET_ARTICLE_LIST](state, { list, total }) { 11 | state.articleTotal = total; 12 | state.articleList = list; 13 | }, 14 | [SET_ARTICLE_DETAIL](state, data) { 15 | state.article = data; 16 | }, 17 | [SET_SAVE_ARTICLE](state, data) { 18 | state.articleTotal += 1; 19 | state.articleList = [data].concat(state.articleList); 20 | } 21 | }; 22 | export default mutations; 23 | -------------------------------------------------------------------------------- /app/web/component/layout/admin/footer/footer.vue: -------------------------------------------------------------------------------- 1 | 11 | 14 | 19 | -------------------------------------------------------------------------------- /app/web/framework/vue/entry/client.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import '../filter'; 3 | import '../directive'; 4 | import '../component'; 5 | 6 | declare global { 7 | interface Window { __INITIAL_STATE__: any; } 8 | } 9 | 10 | export default function(options) { 11 | const state = window.__INITIAL_STATE__ || {}; 12 | Vue.prototype.$http = require('axios'); 13 | if (options.store) { 14 | options.store.replaceState(state); 15 | } else if (state) { 16 | options.data = Object.assign(state, options.data && options.data()); 17 | } 18 | const app = new Vue(options); 19 | app.$mount('#app'); 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "editor.detectIndentation": false, 4 | "eslint.autoFixOnSave": true, 5 | "files.exclude": { 6 | "USE_GITIGNORE": true, 7 | "**/*.js": { 8 | "when": "$(basename).ts" 9 | } 10 | }, 11 | "path-intellisense.mappings": { 12 | "lib": "${workspaceRoot}/app/web/lib", 13 | "asset": "${workspaceRoot}/app/web/asset", 14 | "component": "${workspaceRoot}/app/web/component", 15 | "page": "${workspaceRoot}/app/web/page", 16 | "store": "${workspaceRoot}/app/web/store", 17 | }, 18 | "typescript.tsdk": "node_modules/typescript/lib" 19 | } -------------------------------------------------------------------------------- /app/web/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Egg + Vue + Webpack 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | -------------------------------------------------------------------------------- /app/web/page/about/about.vue: -------------------------------------------------------------------------------- 1 | 8 | 11 | 33 | 34 | -------------------------------------------------------------------------------- /app/web/page/admin/login/login.less: -------------------------------------------------------------------------------- 1 | .login{ 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | position: absolute; 6 | height: 100%; 7 | width: 100%; 8 | background-color: #e4e5e6; 9 | .login-form{ 10 | width: 375px; 11 | height: 400px; 12 | padding: 30px; 13 | background-color: white; 14 | text-align: left; 15 | border-radius: 4px; 16 | position: relative; 17 | margin-left: 0; 18 | margin-right: 0; 19 | zoom: 1; 20 | display: block; 21 | .login-header{ 22 | text-align: center; 23 | font-size: 16px; 24 | font-weight: bold; 25 | margin-bottom: 20px; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./config/tsconfig.json", 3 | "compileOnSave": true, 4 | "compilerOptions": { 5 | "target": "es2017", 6 | "module": "commonjs", 7 | /* Experimental Options */ 8 | "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 9 | "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 10 | }, 11 | "include": [ 12 | "index.ts", 13 | "app/**/*.ts", 14 | "config/**/*.ts", 15 | "mock/**/*.ts", 16 | "test/**/*.ts" 17 | ], 18 | "exclude": [ 19 | "public", 20 | "app/web", 21 | "app/public", 22 | "app/view" 23 | ] 24 | } -------------------------------------------------------------------------------- /typings/app/controller/index.d.ts: -------------------------------------------------------------------------------- 1 | // This file was auto created by egg-ts-helper 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import AboutAbout from '../../../app/controller/about/about'; 5 | import AdminAdmin from '../../../app/controller/admin/admin'; 6 | import CategoryCategory from '../../../app/controller/category/category'; 7 | import IndexIndex from '../../../app/controller/index/index'; 8 | 9 | declare module 'egg' { 10 | interface IController { 11 | about: { 12 | about: AboutAbout; 13 | }; 14 | admin: { 15 | admin: AdminAdmin; 16 | }; 17 | category: { 18 | category: CategoryCategory; 19 | }; 20 | index: { 21 | index: IndexIndex; 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/web/page/admin/home/home.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import ElementUI from 'element-ui'; 3 | import { sync } from 'vuex-router-sync'; 4 | import clientRender from 'client'; 5 | import serverRender from 'server'; 6 | import Layout from 'component/layout/admin'; 7 | import store from './store/app'; 8 | import router from './router'; 9 | import home from './home.vue'; 10 | import '../../../theme/index.css'; 11 | import '../../../asset/css/font-awesome.min.css'; 12 | 13 | Vue.use(ElementUI); 14 | Vue.component(Layout.name, Layout); 15 | sync(store, router); 16 | 17 | const options = { 18 | ...home, 19 | router, 20 | store 21 | }; 22 | 23 | export default EASY_ENV_IS_NODE ? serverRender(options) : clientRender(options); 24 | -------------------------------------------------------------------------------- /app/web/page/category/category.vue: -------------------------------------------------------------------------------- 1 | 8 | 11 | 34 | 35 | -------------------------------------------------------------------------------- /app/web/theme/reset.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";body{font-family:"Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;font-weight:400;font-size:14px;color:#000}a{color:#3C8DBC;text-decoration:none}a:focus,a:hover{color:rgb(99, 164, 201)}a:active{color:#3a8ee6}h1,h2,h3,h4,h5,h6{color:#5a5e66;font-weight:inherit}h1:first-child,h2:first-child,h3:first-child,h4:first-child,h5:first-child,h6:first-child,p:first-child{margin-top:0}h1:last-child,h2:last-child,h3:last-child,h4:last-child,h5:last-child,h6:last-child,p:last-child{margin-bottom:0}h1{font-size:20px}h2{font-size:18px}h3{font-size:16px}h4,h5,h6,p{font-size:inherit}p{line-height:1.8}sub,sup{font-size:13px}small{font-size:12px}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee} -------------------------------------------------------------------------------- /app/web/page/admin/home/view/detail.vue: -------------------------------------------------------------------------------- 1 | 7 | 10 | 26 | -------------------------------------------------------------------------------- /app/router.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Application } from 'egg'; 3 | 4 | export default (application: Application) => { 5 | const { router, controller } = application; 6 | router.get('/', controller.index.index.index); 7 | router.get('/client', controller.index.index.client); 8 | router.get('/list', controller.index.index.list); 9 | router.get('/category', controller.category.category.index); 10 | router.get('/about', controller.about.about.index); 11 | router.get('/login', controller.admin.admin.login); 12 | router.post('/admin/api/article/list', controller.admin.admin.list); 13 | router.post('/admin/api/article/add', controller.admin.admin.add); 14 | router.get('/admin/api/article/:id', controller.admin.admin.detail); 15 | router.get('/admin(/.+)?', controller.admin.admin.home); 16 | }; -------------------------------------------------------------------------------- /app/lib/db/base.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import Condition from '../condition'; 3 | import * as shortid from 'shortid'; 4 | export default class DB { 5 | public instance; 6 | public name: string; 7 | constructor(name: string = 'blog.json') { 8 | this.name = name; 9 | } 10 | 11 | public getUniqueId() { 12 | return shortid.generate(); 13 | } 14 | 15 | public get(collectionName: string) { 16 | return null; 17 | } 18 | 19 | public add(collectionName: string, json: object) { 20 | return null; 21 | } 22 | 23 | public update(collectionName: string, where: object, json: object) { 24 | return null; 25 | } 26 | 27 | public delete(collectionName: string, field: number | string) { 28 | return null; 29 | } 30 | 31 | public getPager(collectionName: string, condition: Condition) { 32 | return null; 33 | } 34 | } -------------------------------------------------------------------------------- /app/web/page/admin/home/router/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import VueRouter from 'vue-router'; 4 | import Dashboard from '../view/dashboard/index.vue'; 5 | import ArticleList from '../view/list.vue'; 6 | 7 | Vue.use(VueRouter); 8 | 9 | export default new VueRouter({ 10 | mode: 'history', 11 | base: '/admin/', 12 | routes: [ 13 | { 14 | path: '/', 15 | component: Dashboard 16 | }, 17 | { 18 | path: '/article/list', 19 | component: ArticleList 20 | }, 21 | { 22 | path: '/article/add', 23 | component: () => import('../view/write/index.vue') 24 | }, 25 | { 26 | path: '/article/detail/:id', 27 | component: () => import('../view/detail.vue') 28 | }, 29 | { 30 | path: '*', component: () => import('../view/notfound.vue') 31 | } 32 | ] 33 | }); 34 | -------------------------------------------------------------------------------- /app/web/theme/row.css: -------------------------------------------------------------------------------- 1 | .el-row{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box}.el-row::after,.el-row::before{display:table;content:""}.el-row::after{clear:both}.el-row--flex{display:-webkit-box;display:-ms-flexbox;display:flex}.el-row--flex:after,.el-row--flex:before{display:none}.el-row--flex.is-justify-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-row--flex.is-justify-end{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.el-row--flex.is-justify-space-between{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.el-row--flex.is-justify-space-around{-ms-flex-pack:distribute;justify-content:space-around}.el-row--flex.is-align-middle{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-row--flex.is-align-bottom{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end} -------------------------------------------------------------------------------- /app/web/theme/spinner.css: -------------------------------------------------------------------------------- 1 | .el-time-spinner{width:100%;white-space:nowrap}.el-spinner{display:inline-block;vertical-align:middle}.el-spinner-inner{-webkit-animation:rotate 2s linear infinite;animation:rotate 2s linear infinite;width:50px;height:50px}.el-spinner-inner .path{stroke:#ececec;stroke-linecap:round;-webkit-animation:dash 1.5s ease-in-out infinite;animation:dash 1.5s ease-in-out infinite}@-webkit-keyframes rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}100%{stroke-dasharray:90,150;stroke-dashoffset:-124}}@keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}100%{stroke-dasharray:90,150;stroke-dashoffset:-124}} -------------------------------------------------------------------------------- /app/lib/condition.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import { JsonProperty } from '@hubcarl/json-typescript-mapper'; 3 | export default class Condition { 4 | @JsonProperty('title') 5 | public title: string; 6 | @JsonProperty('categoryId') 7 | public categoryId: number; 8 | @JsonProperty('status') 9 | public status: number; 10 | @JsonProperty('tag') 11 | public tag: string; 12 | @JsonProperty('pageIndex') 13 | public pageIndex: number; 14 | @JsonProperty('pageSize') 15 | public pageSize: number; 16 | public where: any = {}; 17 | public like: any = {}; 18 | public orderByField: string = 'createTime'; 19 | public orderBy: string = 'desc'; 20 | 21 | constructor() { 22 | this.title = undefined; 23 | this.categoryId = undefined; 24 | this.status = undefined; 25 | this.tag = undefined; 26 | this.pageIndex = 1; 27 | this.pageSize = 10; 28 | this.where = {}; 29 | this.like = {}; 30 | } 31 | } -------------------------------------------------------------------------------- /app/web/component/layout/admin/menu/index.ts: -------------------------------------------------------------------------------- 1 | const menu = { 2 | home: { 3 | name: '首页', 4 | path: '/', 5 | icon: 'fa fa-home', 6 | }, 7 | content: { 8 | name: '内容管理', 9 | icon: 'fa fa-file', 10 | children: { 11 | list: { 12 | name: '文章管理', 13 | path: '/article/list' 14 | }, 15 | add: { 16 | name: '添加文章', 17 | path: '/article/add' 18 | } 19 | } 20 | }, 21 | learn: { 22 | name: '学习资料', 23 | icon: 'fa fa-file', 24 | children: { 25 | Egg: { 26 | name: 'Egg学习', 27 | path: '/' 28 | }, 29 | Vue: { 30 | name: 'Vue学习', 31 | path: '/' 32 | }, 33 | TypeScript: { 34 | name: 'TypeScript', 35 | path: '/' 36 | }, 37 | EasyWebpack: { 38 | name: 'easywebpack', 39 | path: '/' 40 | } 41 | } 42 | } 43 | }; 44 | 45 | export default menu; -------------------------------------------------------------------------------- /app/index.td.ts: -------------------------------------------------------------------------------- 1 | import DB from './lib/db/base'; 2 | 3 | import AboutController from './controller/about/about'; 4 | import AdminController from './controller/admin/admin'; 5 | import CategoryController from './controller/category/category'; 6 | import IndexController from './controller/index/index'; 7 | 8 | import ArticleService from './service/article'; 9 | 10 | declare module 'egg' { 11 | interface Application { 12 | db: DB; 13 | } 14 | 15 | interface Context { 16 | db: DB; 17 | } 18 | 19 | interface IController { 20 | index: { 21 | index: IndexController, 22 | }; 23 | category: { 24 | category: CategoryController, 25 | }; 26 | about: { 27 | about: AboutController, 28 | }; 29 | admin: { 30 | admin: AdminController, 31 | }; 32 | } 33 | 34 | interface IService { 35 | article: ArticleService; 36 | } 37 | 38 | function startCluster(options: any); 39 | } -------------------------------------------------------------------------------- /app/web/theme/breadcrumb.css: -------------------------------------------------------------------------------- 1 | .el-breadcrumb{font-size:14px;line-height:1}.el-breadcrumb::after,.el-breadcrumb::before{display:table;content:""}.el-breadcrumb::after{clear:both}.el-breadcrumb__separator{margin:0 9px;font-weight:700;color:#b4bccc}.el-breadcrumb__separator[class*=icon]{margin:0 6px;font-weight:400}.el-breadcrumb__item{float:left}.el-breadcrumb__inner,.el-breadcrumb__inner a{font-weight:700;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1);color:#2d2f33}.el-breadcrumb__inner a:hover,.el-breadcrumb__inner:hover{color:#3C8DBC;cursor:pointer}.el-breadcrumb__item:last-child .el-breadcrumb__inner,.el-breadcrumb__item:last-child .el-breadcrumb__inner a,.el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover,.el-breadcrumb__item:last-child .el-breadcrumb__inner:hover{font-weight:400;color:#5a5e66;cursor:text}.el-breadcrumb__item:last-child .el-breadcrumb__separator{display:none} -------------------------------------------------------------------------------- /app/model/article.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { JsonProperty } from '@hubcarl/json-typescript-mapper'; 4 | 5 | export default class Article { 6 | @JsonProperty('id') 7 | public id: string; 8 | @JsonProperty('title') 9 | public title: string; 10 | @JsonProperty('summary') 11 | public summary: string; 12 | @JsonProperty('categoryId') 13 | public categoryId: number; 14 | @JsonProperty('tag') 15 | public tag: string; 16 | @JsonProperty('categoryId') 17 | public authorId: number; 18 | @JsonProperty('createTime') 19 | public createTime: number; 20 | @JsonProperty('hits') 21 | public hits: number; 22 | @JsonProperty('url') 23 | public url: string; 24 | @JsonProperty('status') 25 | public status: number; 26 | 27 | constructor() { 28 | this.id = void 0; 29 | this.title = undefined; 30 | this.summary = undefined; 31 | this.tag = undefined; 32 | this.hits = 0; 33 | this.createTime = Date.now(); 34 | } 35 | } -------------------------------------------------------------------------------- /app/web/theme/display.css: -------------------------------------------------------------------------------- 1 | @media only screen and (max-width:767px){.hidden-xs-only{display:none!important}}@media only screen and (min-width:768px){.hidden-sm-and-up{display:none!important}}@media only screen and (min-width:768px) and (max-width:991px){.hidden-sm-only{display:none!important}}@media only screen and (max-width:991px){.hidden-sm-and-down{display:none!important}}@media only screen and (min-width:992px){.hidden-md-and-up{display:none!important}}@media only screen and (min-width:992px) and (max-width:1199px){.hidden-md-only{display:none!important}}@media only screen and (max-width:1199px){.hidden-md-and-down{display:none!important}}@media only screen and (min-width:1200px){.hidden-lg-and-up{display:none!important}}@media only screen and (min-width:1200px) and (max-width:1919px){.hidden-lg-only{display:none!important}}@media only screen and (max-width:1919px){.hidden-lg-and-down{display:none!important}}@media only screen and (min-width:1920px){.hidden-xl-only{display:none!important}} -------------------------------------------------------------------------------- /typings/config/index.d.ts: -------------------------------------------------------------------------------- 1 | // This file was auto created by egg-ts-helper 2 | // Do not modify this file!!!!!!!!! 3 | 4 | import { EggAppConfig } from 'egg'; 5 | import ExportConfigDefault from '../../config/config.default'; 6 | import ExportConfigLocal from '../../config/config.local'; 7 | import ExportConfigProd from '../../config/config.prod'; 8 | import ExportConfigTest from '../../config/config.test'; 9 | type ConfigDefault = ReturnType; 10 | type ConfigLocal = ReturnType; 11 | type ConfigProd = ReturnType; 12 | type ConfigTest = ReturnType; 13 | type NewEggAppConfig = EggAppConfig & ConfigDefault & ConfigLocal & ConfigProd & ConfigTest; 14 | 15 | declare module 'egg' { 16 | interface Application { 17 | config: NewEggAppConfig; 18 | } 19 | 20 | interface Controller { 21 | config: NewEggAppConfig; 22 | } 23 | 24 | interface Service { 25 | config: NewEggAppConfig; 26 | } 27 | } -------------------------------------------------------------------------------- /app/web/view/layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= htmlWebpackPlugin.options.title %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <% htmlWebpackPlugin.options.css.forEach(function(href){ %> 13 | 14 | <% }) %> 15 | 16 | 17 | 18 |
19 | <% htmlWebpackPlugin.options.js.forEach(function(src){ %> 20 | 21 | <% }) %> 22 | 23 | -------------------------------------------------------------------------------- /app/controller/admin/admin.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from 'egg'; 2 | import { deserialize } from '@hubcarl/json-typescript-mapper'; 3 | import Article from '../../model/article'; 4 | import Condition from '../../lib/condition'; 5 | export default class AdminController extends Controller { 6 | 7 | public async login(ctx) { 8 | await ctx.renderClient('admin/login/login.js', {}); 9 | } 10 | 11 | public async home(ctx) { 12 | await ctx.render('admin/home/home.js', { url: this.ctx.url.replace(/\/admin/, '') }); 13 | } 14 | 15 | public async list(ctx) { 16 | const condition = deserialize(Condition, ctx.request.body); 17 | console.log(condition); 18 | this.ctx.body = ctx.service.article.getArtilceList(condition); 19 | } 20 | 21 | public async add(ctx) { 22 | const article = deserialize(Article, ctx.request.body); 23 | ctx.body = this.service.article.saveArticle(article); 24 | } 25 | 26 | public async detail(ctx) { 27 | const id = ctx.query.id; 28 | ctx.body = {}; 29 | } 30 | } -------------------------------------------------------------------------------- /app/web/theme/scrollbar.css: -------------------------------------------------------------------------------- 1 | .el-scrollbar{overflow:hidden;position:relative}.el-scrollbar:active>.el-scrollbar__bar,.el-scrollbar:focus>.el-scrollbar__bar,.el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity 340ms ease-out;transition:opacity 340ms ease-out}.el-scrollbar__wrap{overflow:scroll;height:100%}.el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(135,141,153,.3);-webkit-transition:.3s background-color;transition:.3s background-color}.el-scrollbar__thumb:hover{background-color:rgba(135,141,153,.5)}.el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity 120ms ease-out;transition:opacity 120ms ease-out}.el-scrollbar__bar.is-vertical{width:6px;top:2px}.el-scrollbar__bar.is-vertical>div{width:100%}.el-scrollbar__bar.is-horizontal{height:6px;left:2px}.el-scrollbar__bar.is-horizontal>div{height:100%} -------------------------------------------------------------------------------- /app/web/theme/carousel-item.css: -------------------------------------------------------------------------------- 1 | .el-carousel__item,.el-carousel__mask{position:absolute;height:100%;top:0;left:0}.el-carousel__item{width:100%;display:inline-block;overflow:hidden;z-index:0}.el-carousel__item.is-active{z-index:2}.el-carousel__item.is-animating{-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.el-carousel__item--card{width:50%;-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.el-carousel__item--card.is-in-stage{cursor:pointer;z-index:1}.el-carousel__item--card.is-in-stage.is-hover .el-carousel__mask,.el-carousel__item--card.is-in-stage:hover .el-carousel__mask{opacity:.12}.el-carousel__item--card.is-active{z-index:2}.el-carousel__mask{width:100%;background-color:#fff;opacity:.24;-webkit-transition:.2s;transition:.2s} -------------------------------------------------------------------------------- /app/lib/db/collection.ts: -------------------------------------------------------------------------------- 1 | import DB from './base'; 2 | import Condition from '../condition'; 3 | export default class Collection { 4 | private db: DB; 5 | private name: string; 6 | constructor(db: DB, name: string) { 7 | this.db = db; 8 | this.name = name; 9 | } 10 | 11 | public get() { 12 | return this.db.get(this.name); 13 | } 14 | 15 | public add(json: object) { 16 | return this.db.add(this.name, json); 17 | } 18 | 19 | public update(where: object, json: object) { 20 | return this.db.update(this.name, where, json); 21 | } 22 | 23 | public delete(field: number | string) { 24 | return this.db.delete(this.name, field); 25 | } 26 | 27 | public getPager(condition: Condition) { 28 | return this.db.getPager(this.name, condition); 29 | } 30 | 31 | public getOrderAscByField(field: string) { 32 | return this.get().orderBy(field, 'asc').value(); 33 | } 34 | 35 | public getOrderDescByField(field: string) { 36 | return this.get().orderBy(field, 'desc').value(); 37 | } 38 | } -------------------------------------------------------------------------------- /config/config.local.ts: -------------------------------------------------------------------------------- 1 | import { Application, EggAppConfig } from 'egg'; 2 | import * as ip from 'ip'; 3 | import * as path from 'path'; 4 | 5 | export default (app: EggAppConfig) => { 6 | const exports: any = {}; 7 | 8 | exports.static = { 9 | maxAge: 0, // maxAge 缓存,默认 1 年 10 | }; 11 | 12 | exports.development = { 13 | watchDirs: ['build'], // 指定监视的目录(包括子目录),当目录下的文件变化的时候自动重载应用,路径从项目根目录开始写 14 | ignoreDirs: ['app/web', 'public', 'config'], // 指定过滤的目录(包括子目录) 15 | }; 16 | 17 | exports.webpack = { 18 | browser: 'http://localhost:7001', 19 | }; 20 | 21 | exports.logview = { 22 | dir: path.join(app.baseDir, 'logs'), 23 | }; 24 | 25 | const localIP = ip.address(); 26 | const domainWhiteList = []; 27 | [7001, 9000, 9001].forEach((port) => { 28 | domainWhiteList.push(`http://localhost:${port}`); 29 | domainWhiteList.push(`http://127.0.0.1:${port}`); 30 | domainWhiteList.push(`http://${localIP}:${port}`); 31 | }); 32 | 33 | exports.security = { domainWhiteList }; 34 | 35 | return exports; 36 | }; 37 | -------------------------------------------------------------------------------- /app/web/component/layout/admin/main.vue: -------------------------------------------------------------------------------- 1 | 9 | 33 | 43 | -------------------------------------------------------------------------------- /app/web/component/layout/index/footer/footer.vue: -------------------------------------------------------------------------------- 1 | 24 | 27 | 32 | -------------------------------------------------------------------------------- /app/controller/index/index.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import { Controller } from 'egg'; 3 | import Condition from '../../lib/condition'; 4 | export default class IndexController extends Controller { 5 | 6 | public async index() { 7 | const condition: Condition = new Condition(); 8 | condition.pageIndex = 1; 9 | const result = this.service.article.getArtilceList(condition); 10 | await this.ctx.render('index/index.js', result); 11 | } 12 | 13 | public async client() { // 前端渲染 14 | const condition: Condition = new Condition(); 15 | condition.pageIndex = 1; 16 | const result = this.service.article.getArtilceList(condition); 17 | await this.ctx.renderClient('index/index.js', result); 18 | } 19 | 20 | public async list() { 21 | const { pageIndex, pageSize } = this.ctx.query; 22 | const condition: Condition = new Condition(); 23 | condition.pageIndex = pageIndex; 24 | condition.pageSize = pageSize; 25 | this.ctx.body = this.service.article.getArtilceList(condition); 26 | } 27 | 28 | public async detail() { 29 | const id = this.ctx.query.id; 30 | } 31 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | egg: true, 4 | framework: 'vue', 5 | entry: { 6 | include: ['app/web/page', 7 | { 8 | 'admin/login/login': 'app/web/page/admin/login/login.ts?loader=false', 9 | 'admin/home/home': 'app/web/page/admin/home/home.ts?loader=false' 10 | } 11 | ], 12 | exclude: [/app\/web\/page\/admin\/home\/(component|view|router|store)/], 13 | loader: { 14 | client: 'app/web/framework/vue/entry/client-loader.ts', 15 | server: 'app/web/framework/vue/entry/server-loader.ts', 16 | } 17 | }, 18 | alias: { 19 | server: 'app/web/framework/vue/entry/server.ts', 20 | client: 'app/web/framework/vue/entry/client.ts', 21 | asset: 'app/web/asset', 22 | component: 'app/web/component', 23 | framework: 'app/web/framework', 24 | store: 'app/web/store', 25 | vue: 'vue/dist/vue.esm.js' 26 | }, 27 | dll: ['vue', 'axios', 'vuex', 'vuex-router-sync'], 28 | loaders: { 29 | less: { 30 | framework: true 31 | }, 32 | typescript: true 33 | }, 34 | plugins: { 35 | 36 | }, 37 | done() { 38 | 39 | } 40 | }; -------------------------------------------------------------------------------- /config/config.default.ts: -------------------------------------------------------------------------------- 1 | import { Application, EggAppConfig } from 'egg'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | 5 | export default (app: EggAppConfig) => { 6 | const exports: any = {}; 7 | 8 | exports.siteFile = { 9 | '/favicon.ico': fs.readFileSync(path.join(app.baseDir, 'app/web/asset/images/favicon.ico')), 10 | '/logo.png': fs.readFileSync(path.join(app.baseDir, 'app/web/asset/images/logo.png')), 11 | }; 12 | 13 | exports.view = { 14 | cache: false, 15 | }; 16 | 17 | exports.vuessr = { 18 | layout: path.join(app.baseDir, 'app/web/view/layout.html'), 19 | renderOptions: { 20 | // 告诉 vue-server-renderer 去 app/view 查找异步 chunk 文件 21 | basedir: path.join(app.baseDir, 'app/view'), 22 | }, 23 | }; 24 | 25 | exports.logger = { 26 | consoleLevel: 'DEBUG', 27 | dir: path.join(app.baseDir, 'logs'), 28 | }; 29 | 30 | exports.static = { 31 | prefix: '/public/', 32 | dir: path.join(app.baseDir, 'public'), 33 | }; 34 | 35 | exports.keys = '123456'; 36 | 37 | exports.middleware = [ 38 | 'access', 39 | ]; 40 | 41 | return exports; 42 | }; 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 sky. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/web/framework/vue/entry/server.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import '../filter'; 3 | import '../directive'; 4 | import '../component'; 5 | 6 | export default function render(options) { 7 | if (options.store && options.router) { 8 | return (context) => { 9 | options.store.replaceState({ ...options.store.state, ...context.state }); 10 | options.router.push({ path: context.state.url }); 11 | const matchedComponents = options.router.getMatchedComponents(); 12 | if (!matchedComponents) { 13 | return Promise.reject({ code: '404' }); 14 | } 15 | return Promise.all( 16 | matchedComponents.map((component) => { 17 | if (component.preFetch) { 18 | return component.preFetch(options.store); 19 | } 20 | return null; 21 | }) 22 | ).then(() => { 23 | context.state = { ...options.store.state, ...context.state }; 24 | return new Vue(options); 25 | }); 26 | }; 27 | } 28 | return (context) => { 29 | const VueApp = Vue.extend(options); 30 | const app = new VueApp({ data: context.state }); 31 | return Promise.resolve(app); 32 | }; 33 | } -------------------------------------------------------------------------------- /app/web/page/admin/home/store/app/actions.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as Type from './mutation-type'; 4 | import Vue from 'vue'; 5 | import Vuex from 'vuex'; 6 | import axios from 'axios'; 7 | 8 | Vue.use(Vuex); 9 | 10 | const actions = { 11 | 12 | FETCH_ARTICLE_LIST: ({ commit, dispatch, state }, condition) => { 13 | return axios.post(`/admin/api/article/list`, condition, { 14 | headers: { 15 | 'x-csrf-token': state.csrf, 16 | 'Cookie': `csrfToken=${state.csrf}` 17 | } 18 | }).then(response => { 19 | commit(Type.SET_ARTICLE_LIST, response.data); 20 | }); 21 | }, 22 | 23 | FETCH_ARTICLE_DETAIL: ({ commit, dispatch, state }, { id }) => { 24 | return axios.get(`/admin/api/article/${id}`) 25 | .then(response => { 26 | commit(Type.SET_ARTICLE_DETAIL, response.data); 27 | }); 28 | }, 29 | 30 | SAVE_ARTICLE: ({ commit, dispatch, state }, data) => { 31 | return axios.post(`/admin/api/article/add`, data, { 32 | headers: { 33 | 'x-csrf-token': state.csrf, 34 | } 35 | }).then(response => { 36 | commit(Type.SET_ARTICLE_LIST, data); 37 | }); 38 | }, 39 | 40 | }; 41 | 42 | export default actions; -------------------------------------------------------------------------------- /app/web/theme/collapse.css: -------------------------------------------------------------------------------- 1 | .el-collapse-item__header,.el-collapse-item__wrap{background-color:#fff;border-bottom:1px solid #e6ebf5}.el-collapse,.el-collapse-item__header,.el-collapse-item__wrap{border-bottom:1px solid #e6ebf5}.el-collapse{border-top:1px solid #e6ebf5}.el-collapse-item__header{height:48px;line-height:48px;color:#2d2f33;cursor:pointer;font-size:13px;font-weight:500;-webkit-transition:border-bottom-color .3s;transition:border-bottom-color .3s;outline:0}.el-collapse-item__arrow{margin-right:8px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;float:right;line-height:48px;font-weight:300}.el-collapse-item__header.focusing:focus:not(:hover){color:#3C8DBC}.el-collapse-item__wrap{will-change:height;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box}.el-collapse-item__content{padding-bottom:25px;font-size:13px;color:#2d2f33;line-height:1.769230769230769}.el-collapse-item.is-active .el-collapse-item__header{border-bottom-color:transparent}.el-collapse-item.is-active .el-collapse-item__header .el-collapse-item__arrow{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-collapse-item:last-child{margin-bottom:-1px} -------------------------------------------------------------------------------- /app/web/component/layout/admin/header/header.less: -------------------------------------------------------------------------------- 1 | .header { 2 | width: 100%; 3 | position: fixed; 4 | display: flex; 5 | height: 50px; 6 | background-color: #3c8dbc; 7 | z-index: 10; 8 | .logo { 9 | .min { 10 | display: none; 11 | } 12 | width: 230px; 13 | height: 50px; 14 | text-align: center; 15 | line-height: 50px; 16 | color: #fff; 17 | background-color: #367fa9; 18 | -webkit-transition: width 0.35s; 19 | transition: width 0.35s; 20 | } 21 | .right { 22 | position: absolute; 23 | right: 0; 24 | } 25 | .header-btn { 26 | .el-badge__content { 27 | top: 14px; 28 | right: 7px; 29 | text-align: center; 30 | font-size: 9px; 31 | padding: 0 3px; 32 | background-color: #00a65a; 33 | color: #fff; 34 | border: none; 35 | white-space: nowrap; 36 | vertical-align: baseline; 37 | border-radius: .25em; 38 | } 39 | overflow: hidden; 40 | height: 50px; 41 | display: inline-block; 42 | text-align: center; 43 | line-height: 50px; 44 | cursor: pointer; 45 | padding: 0 14px; 46 | color: #fff; 47 | &:hover { 48 | background-color: #367fa9 49 | } 50 | } 51 | } 52 | 53 | .menu { 54 | border-right: none; 55 | } -------------------------------------------------------------------------------- /app/web/component/layout/index/header/header.less: -------------------------------------------------------------------------------- 1 | .header { 2 | width: 100%; 3 | position: fixed; 4 | display: flex; 5 | height: 50px; 6 | background-color: #3c8dbc; 7 | z-index: 10; 8 | .logo { 9 | .min { 10 | display: none; 11 | } 12 | width: 230px; 13 | height: 50px; 14 | text-align: center; 15 | line-height: 50px; 16 | color: #fff; 17 | background-color: #367fa9; 18 | -webkit-transition: width 0.35s; 19 | transition: width 0.35s; 20 | } 21 | .right { 22 | position: absolute; 23 | right: 0; 24 | } 25 | .header-btn { 26 | .el-badge__content { 27 | top: 14px; 28 | right: 7px; 29 | text-align: center; 30 | font-size: 9px; 31 | padding: 0 3px; 32 | background-color: #00a65a; 33 | color: #fff; 34 | border: none; 35 | white-space: nowrap; 36 | vertical-align: baseline; 37 | border-radius: .25em; 38 | } 39 | overflow: hidden; 40 | height: 50px; 41 | display: inline-block; 42 | text-align: center; 43 | line-height: 50px; 44 | cursor: pointer; 45 | padding: 0 14px; 46 | color: #fff; 47 | &:hover { 48 | background-color: #367fa9 49 | } 50 | } 51 | } 52 | 53 | .menu { 54 | border-right: none; 55 | } -------------------------------------------------------------------------------- /app/middleware/access.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as util from 'util'; 3 | export default () => { 4 | const skipExt = [ '.png', '.jpeg', '.jpg', '.ico', '.gif' ]; 5 | return function *(next) { 6 | const start = new Date().getTime(); 7 | 8 | yield * next; 9 | 10 | const rs = Math.ceil(new Date().getTime() - start); 11 | 12 | this.set('X-Response-Time', rs); 13 | 14 | const ext = path.extname(this.url).toLocaleLowerCase(); 15 | const isSkip = skipExt.indexOf(ext) !== -1 && this.status < 400; 16 | 17 | if (!isSkip) { 18 | const ip = this.get('X-Real-IP') || this.ip; 19 | const port = this.get('X-Real-Port'); 20 | const protocol = this.protocol.toUpperCase(); 21 | const method = this.method; 22 | const url = this.url; 23 | const status = this.status; 24 | const length = this.length || '-'; 25 | const referrer = this.get('referrer') || '-'; 26 | const ua = this.get('user-agent') || '-'; 27 | const serverTime = this.response.get('X-Server-Response-Time') || '-'; 28 | const message = util.format('[access] %s:%s - %s %s %s/%s %s %s %s %s %s', 29 | ip, port, method, url, protocol, status, length, referrer, rs, serverTime, ua); 30 | this.logger.info(message); 31 | } 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /app/service/article.ts: -------------------------------------------------------------------------------- 1 | import { Context, Service } from 'egg'; 2 | import { deserialize } from '@hubcarl/json-typescript-mapper'; 3 | import Colllection from '../lib/db/collection'; 4 | import Article from '../model/article'; 5 | import Condition from '../lib/condition'; 6 | const ARTICLE_COLLECTION = Symbol.for('ArticeService#Collection'); 7 | 8 | export default class ArticeService extends Service { 9 | private colllection: Colllection; 10 | constructor(ctx: Context) { 11 | super(ctx); 12 | this.ctx = ctx; 13 | this.colllection = new Colllection(ctx.db, 'article'); 14 | } 15 | 16 | public getArtilceList(condition: Condition) { 17 | if (condition.categoryId) { 18 | condition.where.categoryId = condition.categoryId; 19 | } 20 | if (condition.status) { 21 | condition.where.status = condition.status; 22 | } 23 | if (condition.title) { 24 | condition.like.title = condition.title; 25 | } 26 | return this.colllection.getPager(condition); 27 | } 28 | 29 | public saveArticle(data: object) { 30 | const article: Article = deserialize(Article, data); 31 | if (article.id) { 32 | return this.colllection.update({ id: article.id }, article); 33 | } 34 | article.id = this.ctx.db.getUniqueId(); 35 | return this.colllection.add(article); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/web/theme/alert.css: -------------------------------------------------------------------------------- 1 | .el-alert{width:100%;padding:8px 16px;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;position:relative;background-color:#fff;overflow:hidden;opacity:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transition:opacity .2s;transition:opacity .2s}.el-alert.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-alert--success{background-color:#f0f9eb;color:#67c23a}.el-alert--success .el-alert__description{color:#67c23a}.el-alert--info{background-color:#f3f4f5;color:#878d99}.el-alert--info .el-alert__description{color:#878d99}.el-alert--warning{background-color:#fdf5e6;color:#eb9e05}.el-alert--warning .el-alert__description{color:#eb9e05}.el-alert--error{background-color:#fee;color:#fa5555}.el-alert--error .el-alert__description{color:#fa5555}.el-alert__content{display:table-cell;padding:0 8px}.el-alert__icon{font-size:16px;width:16px}.el-alert__icon.is-big{font-size:28px;width:28px}.el-alert__title{font-size:13px;line-height:18px}.el-alert__title.is-bold{font-weight:700}.el-alert .el-alert__description{font-size:12px;margin:5px 0 0}.el-alert__closebtn{font-size:12px;color:#b4bccc;opacity:1;position:absolute;top:12px;right:15px;cursor:pointer}.el-alert__closebtn.is-customed{font-style:normal;font-size:13px;top:9px}.el-alert-fade-enter,.el-alert-fade-leave-active{opacity:0} -------------------------------------------------------------------------------- /app/web/theme/tree.css: -------------------------------------------------------------------------------- 1 | .el-tree{cursor:default;background:#fff;color:#5a5e66}.el-tree__empty-block{position:relative;min-height:60px;text-align:center;width:100%;height:100%}.el-tree__empty-text{position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#6f7180}.el-tree-node{white-space:nowrap}.el-tree-node__content{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:26px;cursor:pointer}.el-tree-node__content>.el-tree-node__expand-icon{padding:6px}.el-tree-node__content>.el-checkbox{margin-right:8px}.el-tree-node__content:hover{background-color:#f5f7fa}.el-tree-node__expand-icon{cursor:pointer;color:#b4bccc;font-size:12px;-webkit-transform:rotate(0);transform:rotate(0);-webkit-transition:-webkit-transform .3s ease-in-out;transition:-webkit-transform .3s ease-in-out;transition:transform .3s ease-in-out;transition:transform .3s ease-in-out,-webkit-transform .3s ease-in-out}.el-tree-node__expand-icon.expanded{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-tree-node__expand-icon.is-leaf{color:transparent;cursor:default}.el-tree-node__label{font-size:14px}.el-tree-node__loading-icon{margin-right:8px;font-size:14px;color:#b4bccc}.el-tree-node>.el-tree-node__children{overflow:hidden;background-color:transparent}.el-tree-node.is-expanded>.el-tree-node__children{display:block}.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content{background-color:#f0f7ff} -------------------------------------------------------------------------------- /app/web/theme/popper.css: -------------------------------------------------------------------------------- 1 | .el-popper .popper__arrow,.el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.el-popper .popper__arrow::after{content:" ";border-width:6px}.el-popper[x-placement^=top]{margin-bottom:12px}.el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.el-popper[x-placement^=bottom]{margin-top:12px}.el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.el-popper[x-placement^=right]{margin-left:12px}.el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.el-popper[x-placement^=left]{margin-right:12px}.el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff} -------------------------------------------------------------------------------- /app/web/theme/switch.css: -------------------------------------------------------------------------------- 1 | .el-switch{display:inline-block;position:relative;font-size:14px;line-height:20px;height:20px;vertical-align:middle}.el-switch.is-disabled .el-switch__core,.el-switch.is-disabled .el-switch__label{cursor:not-allowed}.el-switch__core,.el-switch__label{display:inline-block;cursor:pointer;vertical-align:middle}.el-switch__label{-webkit-transition:.2s;transition:.2s;height:20px;font-size:14px;font-weight:500;color:#2d2f33}.el-switch__label.is-active{color:#3C8DBC}.el-switch__label--left{margin-right:10px}.el-switch__label--right{margin-left:10px}.el-switch__label *{line-height:1;font-size:14px;display:inline-block}.el-switch__input{position:absolute;width:0;height:0;opacity:0;margin:0}.el-switch__input:focus~.el-switch__core{outline:#3C8DBC solid 1px}.el-switch__core{margin:0;position:relative;width:40px;height:20px;border:1px solid #d8dce5;outline:0;border-radius:10px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#d8dce5;-webkit-transition:border-color .3s,background-color .3s;transition:border-color .3s,background-color .3s}.el-switch__core .el-switch__button{position:absolute;top:1px;left:1px;border-radius:100%;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;width:16px;height:16px;background-color:#fff}.el-switch.is-checked .el-switch__core{border-color:#3C8DBC;background-color:#3C8DBC}.el-switch.is-disabled{opacity:.6}.el-switch--wide .el-switch__label.el-switch__label--left span{left:10px}.el-switch--wide .el-switch__label.el-switch__label--right span{right:10px}.el-switch .label-fade-enter,.el-switch .label-fade-leave-active{opacity:0} -------------------------------------------------------------------------------- /app/web/theme/loading.css: -------------------------------------------------------------------------------- 1 | .el-loading-parent--relative{position:relative!important}.el-loading-parent--hidden{overflow:hidden!important}.el-loading-mask{position:absolute;z-index:10000;background-color:rgba(255,255,255,.9);margin:0;top:0;right:0;bottom:0;left:0;-webkit-transition:opacity .3s;transition:opacity .3s}.el-loading-mask.is-fullscreen{position:fixed}.el-loading-mask.is-fullscreen .el-loading-spinner{margin-top:-25px}.el-loading-mask.is-fullscreen .el-loading-spinner .circular{height:50px;width:50px}.el-loading-spinner{top:50%;margin-top:-21px;width:100%;text-align:center;position:absolute}.el-loading-spinner .el-loading-text{color:#3C8DBC;margin:3px 0;font-size:14px}.el-loading-spinner .circular{height:42px;width:42px;-webkit-animation:loading-rotate 2s linear infinite;animation:loading-rotate 2s linear infinite}.el-loading-spinner .path{-webkit-animation:loading-dash 1.5s ease-in-out infinite;animation:loading-dash 1.5s ease-in-out infinite;stroke-dasharray:90,150;stroke-dashoffset:0;stroke-width:2;stroke:#3C8DBC;stroke-linecap:round}.el-loading-spinner i{color:#3C8DBC}.el-loading-fade-enter,.el-loading-fade-leave-active{opacity:0}@-webkit-keyframes loading-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}100%{stroke-dasharray:90,150;stroke-dashoffset:-120px}}@keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}100%{stroke-dasharray:90,150;stroke-dashoffset:-120px}} -------------------------------------------------------------------------------- /app/web/theme/notification.css: -------------------------------------------------------------------------------- 1 | .el-notification{display:-webkit-box;display:-ms-flexbox;display:flex;width:330px;padding:14px 26px 14px 13px;border-radius:8px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #e6ebf5;position:fixed;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;overflow:hidden}.el-notification.right{right:16px}.el-notification.left{left:16px}.el-notification__group{margin-left:13px}.el-notification__title{font-weight:700;font-size:16px;color:#2d2f33;margin:0}.el-notification__content{font-size:14px;line-height:21px;margin:6px 0 0;color:#5a5e66;text-align:justify}.el-notification__content p{margin:0}.el-notification__icon{height:24px;width:24px;font-size:24px;-webkit-transform:translateY(4px);transform:translateY(4px)}.el-notification__closeBtn{position:absolute;top:15px;right:15px;cursor:pointer;color:#878d99;font-size:16px}.el-notification__closeBtn:hover{color:#5a5e66}.el-notification .el-icon-success{color:#67c23a}.el-notification .el-icon-error{color:#fa5555}.el-notification .el-icon-info{color:#878d99}.el-notification .el-icon-warning{color:#eb9e05}.el-notification-fade-enter.right{right:0;-webkit-transform:translateX(100%);transform:translateX(100%)}.el-notification-fade-enter.left{left:0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}.el-notification-fade-leave-active{opacity:0} -------------------------------------------------------------------------------- /app/web/page/admin/login/login.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 64 | 65 | -------------------------------------------------------------------------------- /app/web/theme/message.css: -------------------------------------------------------------------------------- 1 | .el-message__closeBtn:focus,.el-message__content:focus{outline-width:0}.el-message{min-width:380px;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;border-width:1px;border-style:solid;border-color:#e6ebf5;position:fixed;left:50%;top:20px;-webkit-transform:translateX(-50%);transform:translateX(-50%);background-color:#edf2fc;-webkit-transition:opacity .3s,-webkit-transform .4s;transition:opacity .3s,-webkit-transform .4s;transition:opacity .3s,transform .4s;transition:opacity .3s,transform .4s,-webkit-transform .4s;overflow:hidden;padding:15px 15px 15px 20px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-message.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-message p{margin:0}.el-message--info .el-message__content{color:#878d99}.el-message--success{background-color:#f0f9eb;border-color:#e1f3d8}.el-message--success .el-message__content{color:#67c23a}.el-message--warning{background-color:#fdf5e6;border-color:#fbeccd}.el-message--warning .el-message__content{color:#eb9e05}.el-message--error{background-color:#fee;border-color:#fedddd}.el-message--error .el-message__content{color:#fa5555}.el-message__icon{margin-right:10px}.el-message__content{padding:0;font-size:14px;line-height:1}.el-message__closeBtn{position:absolute;top:50%;right:15px;-webkit-transform:translateY(-50%);transform:translateY(-50%);cursor:pointer;color:#b4bccc;font-size:16px}.el-message__closeBtn:hover{color:#878d99}.el-message .el-icon-success{color:#67c23a}.el-message .el-icon-error{color:#fa5555}.el-message .el-icon-info{color:#878d99}.el-message .el-icon-warning{color:#eb9e05}.el-message-fade-enter,.el-message-fade-leave-active{opacity:0;-webkit-transform:translate(-50%,-100%);transform:translate(-50%,-100%)} -------------------------------------------------------------------------------- /app/web/theme/progress.css: -------------------------------------------------------------------------------- 1 | .el-progress{position:relative;line-height:1}.el-progress__text{font-size:14px;color:#5a5e66;display:inline-block;vertical-align:middle;margin-left:10px;line-height:1}.el-progress__text i{vertical-align:middle;display:block}.el-progress--circle{display:inline-block}.el-progress--circle .el-progress__text{position:absolute;top:50%;left:0;width:100%;text-align:center;margin:0;-webkit-transform:translate(0,-50%);transform:translate(0,-50%)}.el-progress--circle .el-progress__text i{vertical-align:middle;display:inline-block}.el-progress--without-text .el-progress__text{display:none}.el-progress--without-text .el-progress-bar{padding-right:0;margin-right:0;display:block}.el-progress-bar,.el-progress-bar__inner::after,.el-progress-bar__innerText{display:inline-block;vertical-align:middle}.el-progress--text-inside .el-progress-bar{padding-right:0;margin-right:0}.el-progress.is-success .el-progress-bar__inner{background-color:#67c23a}.el-progress.is-success .el-progress__text{color:#67c23a}.el-progress.is-exception .el-progress-bar__inner{background-color:#fa5555}.el-progress.is-exception .el-progress__text{color:#fa5555}.el-progress-bar{padding-right:50px;width:100%;margin-right:-55px;-webkit-box-sizing:border-box;box-sizing:border-box}.el-progress-bar__outer{height:6px;border-radius:100px;background-color:#e6ebf5;overflow:hidden;position:relative;vertical-align:middle}.el-progress-bar__inner{position:absolute;left:0;top:0;height:100%;background-color:#3C8DBC;text-align:right;border-radius:100px;line-height:1;white-space:nowrap}.el-progress-bar__inner::after{content:"";height:100%}.el-progress-bar__innerText{color:#fff;font-size:12px;margin:0 5px}@-webkit-keyframes progress{0%{background-position:0 0}100%{background-position:32px 0}}@keyframes progress{0%{background-position:0 0}100%{background-position:32px 0}} -------------------------------------------------------------------------------- /app/web/page/admin/home/view/write/index.vue: -------------------------------------------------------------------------------- 1 | 18 | 23 | -------------------------------------------------------------------------------- /app/web/theme/popover.css: -------------------------------------------------------------------------------- 1 | .el-popper .popper__arrow,.el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.el-popper .popper__arrow::after{content:" ";border-width:6px}.el-popper[x-placement^=top]{margin-bottom:12px}.el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.el-popper[x-placement^=bottom]{margin-top:12px}.el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.el-popper[x-placement^=right]{margin-left:12px}.el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.el-popper[x-placement^=left]{margin-right:12px}.el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.el-popover{position:absolute;background:#fff;min-width:150px;border-radius:4px;border:1px solid #e6ebf5;padding:12px;z-index:2000;color:#5a5e66;line-height:1.4;text-align:justify;word-break:break-all;font-size:14px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-popover--plain{padding:18px 20px}.el-popover__title{color:#2d2f33;font-size:16px;line-height:1;margin-bottom:12px} -------------------------------------------------------------------------------- /app/lib/db/file.ts: -------------------------------------------------------------------------------- 1 | import * as lowdb from 'lowdb'; 2 | import * as lodashid from 'lodash-id'; 3 | import * as FileSync from 'lowdb/adapters/FileSync'; 4 | import BaseDB from './base'; 5 | import Condition from '../condition'; 6 | export default class FileDB extends BaseDB { 7 | public instance; 8 | constructor(name?: string) { 9 | super(name); 10 | const file = new FileSync(this.name); 11 | this.instance = lowdb(file); 12 | this.instance._.mixin(lodashid); 13 | this.create(); 14 | } 15 | 16 | public create() { 17 | this.instance.defaults({ article: [], user: {} }).write(); 18 | } 19 | 20 | public get(collectionName: string) { 21 | return this.instance.get(collectionName); 22 | } 23 | 24 | public add(collectionName: string, json: object) { 25 | return this.get(collectionName) 26 | .push(json) 27 | .write(); 28 | } 29 | 30 | public update(collectionName: string, where: object, json: object) { 31 | return this.get(collectionName).find(where).assign(json).write(); 32 | } 33 | 34 | public delete(collectionName: string, field: number | string) { 35 | return this.get(collectionName).write(); 36 | } 37 | 38 | public getPager(collectionName: string, condition: Condition) { 39 | const { 40 | where, 41 | like, 42 | pageIndex, 43 | pageSize, 44 | orderByField, 45 | orderBy 46 | } = condition; 47 | const start = (pageIndex - 1) * pageSize; 48 | const end = pageIndex * pageSize; 49 | const result = this.get(collectionName) 50 | .filter(where) 51 | .filter(item => { 52 | return Object.keys(like).reduce((isLike, key) => { 53 | return isLike && item[key] && item[key].indexOf(like[key]) > -1; 54 | }, true); 55 | }) 56 | .orderBy(orderByField, orderBy); 57 | const total = result.size().value(); 58 | const list = result.slice(start, end).value(); 59 | return { total, list }; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/web/component/layout/admin/aside/aside.vue: -------------------------------------------------------------------------------- 1 | 28 | 47 | 48 | -------------------------------------------------------------------------------- /app/web/theme/tag.css: -------------------------------------------------------------------------------- 1 | .el-tag{background-color:rgba(64,158,255,.1);display:inline-block;padding:0 10px;height:32px;line-height:30px;font-size:12px;color:#3C8DBC;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid rgba(64,158,255,.2);white-space:nowrap}.el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:18px;width:18px;line-height:18px;vertical-align:middle;top:-1px;right:-5px;color:#3C8DBC}.el-tag .el-icon-close::before{display:block}.el-tag .el-icon-close:hover{background-color:#3C8DBC;color:#fff}.el-tag--info,.el-tag--info .el-tag__close{color:#878d99}.el-tag--info{background-color:rgba(135,141,153,.1);border-color:rgba(135,141,153,.2)}.el-tag--info.is-hit{border-color:#878d99}.el-tag--info .el-tag__close:hover{background-color:#878d99;color:#fff}.el-tag--success{background-color:rgba(103,194,58,.1);border-color:rgba(103,194,58,.2);color:#67c23a}.el-tag--success.is-hit{border-color:#67c23a}.el-tag--success .el-tag__close{color:#67c23a}.el-tag--success .el-tag__close:hover{background-color:#67c23a;color:#fff}.el-tag--warning{background-color:rgba(235,158,5,.1);border-color:rgba(235,158,5,.2);color:#eb9e05}.el-tag--warning.is-hit{border-color:#eb9e05}.el-tag--warning .el-tag__close{color:#eb9e05}.el-tag--warning .el-tag__close:hover{background-color:#eb9e05;color:#fff}.el-tag--danger{background-color:rgba(250,85,85,.1);border-color:rgba(250,85,85,.2);color:#fa5555}.el-tag--danger.is-hit{border-color:#fa5555}.el-tag--danger .el-tag__close{color:#fa5555}.el-tag--danger .el-tag__close:hover{background-color:#fa5555;color:#fff}.el-tag--medium{height:28px;line-height:26px}.el-tag--medium .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--small{height:24px;padding:0 8px;line-height:22px}.el-tag--small .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--mini{height:20px;padding:0 5px;line-height:19px}.el-tag--mini .el-icon-close{margin-left:-3px;-webkit-transform:scale(.7);transform:scale(.7)} -------------------------------------------------------------------------------- /app/web/theme/carousel.css: -------------------------------------------------------------------------------- 1 | .el-carousel{overflow-x:hidden;position:relative}.el-carousel__container{position:relative;height:300px}.el-carousel__arrow{border:none;outline:0;padding:0;margin:0;height:36px;width:36px;cursor:pointer;-webkit-transition:.3s;transition:.3s;border-radius:50%;background-color:rgba(31,45,61,.11);color:#fff;position:absolute;top:50%;z-index:10;-webkit-transform:translateY(-50%);transform:translateY(-50%);text-align:center;font-size:12px}.el-carousel__arrow--left{left:16px}.el-carousel__arrow--right{right:16px}.el-carousel__arrow:hover{background-color:rgba(31,45,61,.23)}.el-carousel__arrow i{cursor:pointer}.el-carousel__indicators{position:absolute;list-style:none;bottom:0;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);margin:0;padding:0;z-index:2}.el-carousel__indicators--outside{bottom:26px;text-align:center;position:static;-webkit-transform:none;transform:none}.el-carousel__indicators--outside .el-carousel__indicator:hover button{opacity:.64}.el-carousel__indicators--outside button{background-color:#b4bccc;opacity:.24}.el-carousel__indicators--labels{left:0;right:0;-webkit-transform:none;transform:none;text-align:center}.el-carousel__indicators--labels .el-carousel__button{height:auto;width:auto;padding:2px 18px;font-size:12px}.el-carousel__indicators--labels .el-carousel__indicator{padding:6px 4px}.el-carousel__indicator{display:inline-block;background-color:transparent;padding:12px 4px;cursor:pointer}.el-carousel__indicator:hover button{opacity:.72}.el-carousel__indicator.is-active button{opacity:1}.el-carousel__button{display:block;opacity:.48;width:30px;height:2px;background-color:#fff;border:none;outline:0;padding:0;margin:0;cursor:pointer;-webkit-transition:.3s;transition:.3s}.carousel-arrow-left-enter,.carousel-arrow-left-leave-active{-webkit-transform:translateY(-50%) translateX(-10px);transform:translateY(-50%) translateX(-10px);opacity:0}.carousel-arrow-right-enter,.carousel-arrow-right-leave-active{-webkit-transform:translateY(-50%) translateX(10px);transform:translateY(-50%) translateX(10px);opacity:0} -------------------------------------------------------------------------------- /app/web/theme/radio-button.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";.el-radio-button,.el-radio-button__inner{display:inline-block;position:relative;outline:0}.el-radio-button__inner{line-height:1;white-space:nowrap;vertical-align:middle;background:#fff;border:1px solid #d8dce5;font-weight:500;border-left:0;color:#5a5e66;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;cursor:pointer;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);padding:12px 20px;font-size:14px;border-radius:0}.el-radio-button__inner.is-round{padding:12px 20px}.el-radio-button__inner:hover{color:#3C8DBC}.el-radio-button__inner [class*=el-icon-]{line-height:.9}.el-radio-button__inner [class*=el-icon-]+span{margin-left:5px}.el-radio-button__orig-radio{opacity:0;outline:0;position:absolute;z-index:-1;left:-999px}.el-radio-button__orig-radio:checked+.el-radio-button__inner{color:#fff;background-color:#3C8DBC;border-color:#3C8DBC;-webkit-box-shadow:-1px 0 0 0 #3C8DBC;box-shadow:-1px 0 0 0 #3C8DBC}.el-radio-button__orig-radio:disabled+.el-radio-button__inner{color:#b4bccc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#e6ebf5;-webkit-box-shadow:none;box-shadow:none}.el-radio-button__orig-radio:disabled:checked+.el-radio-button__inner{background-color:#edf2fc}.el-radio-button:first-child .el-radio-button__inner{border-left:1px solid #d8dce5;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.el-radio-button:last-child .el-radio-button__inner{border-radius:0 4px 4px 0}.el-radio-button:first-child:last-child .el-radio-button__inner{border-radius:4px}.el-radio-button--medium .el-radio-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-radio-button--medium .el-radio-button__inner.is-round{padding:10px 20px}.el-radio-button--small .el-radio-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-radio-button--small .el-radio-button__inner.is-round{padding:9px 15px}.el-radio-button--mini .el-radio-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-radio-button--mini .el-radio-button__inner.is-round{padding:7px 15px}.el-radio-button:focus:not(.is-focus):not(:active){-webkit-box-shadow:0 0 2px 2px #3C8DBC;box-shadow:0 0 2px 2px #3C8DBC} -------------------------------------------------------------------------------- /app/web/page/index/index.vue: -------------------------------------------------------------------------------- 1 | 24 | 27 | 75 | 76 | -------------------------------------------------------------------------------- /app/web/theme/tooltip.css: -------------------------------------------------------------------------------- 1 | .el-tooltip__popper{position:absolute;border-radius:4px;padding:10px;z-index:2000;font-size:12px;line-height:1.2}.el-tooltip__popper .popper__arrow,.el-tooltip__popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-tooltip__popper .popper__arrow{border-width:6px}.el-tooltip__popper .popper__arrow::after{content:" ";border-width:5px}.el-tooltip__popper[x-placement^=top]{margin-bottom:12px}.el-tooltip__popper[x-placement^=top] .popper__arrow{bottom:-6px;border-top-color:#2d2f33;border-bottom-width:0}.el-tooltip__popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-5px;border-top-color:#2d2f33;border-bottom-width:0}.el-tooltip__popper[x-placement^=bottom]{margin-top:12px}.el-tooltip__popper[x-placement^=bottom] .popper__arrow{top:-6px;border-top-width:0;border-bottom-color:#2d2f33}.el-tooltip__popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-5px;border-top-width:0;border-bottom-color:#2d2f33}.el-tooltip__popper[x-placement^=right]{margin-left:12px}.el-tooltip__popper[x-placement^=right] .popper__arrow{left:-6px;border-right-color:#2d2f33;border-left-width:0}.el-tooltip__popper[x-placement^=right] .popper__arrow::after{bottom:-5px;left:1px;border-right-color:#2d2f33;border-left-width:0}.el-tooltip__popper[x-placement^=left]{margin-right:12px}.el-tooltip__popper[x-placement^=left] .popper__arrow{right:-6px;border-right-width:0;border-left-color:#2d2f33}.el-tooltip__popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-5px;margin-left:-5px;border-right-width:0;border-left-color:#2d2f33}.el-tooltip__popper.is-dark{background:#2d2f33;color:#fff}.el-tooltip__popper.is-light{background:#fff;border:1px solid #2d2f33}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow{border-top-color:#2d2f33}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow::after{border-top-color:#fff}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow{border-bottom-color:#2d2f33}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow::after{border-bottom-color:#fff}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow{border-left-color:#2d2f33}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow::after{border-left-color:#fff}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow{border-right-color:#2d2f33}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow::after{border-right-color:#fff} -------------------------------------------------------------------------------- /app/web/component/layout/layout.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { PluginObject } from 'vue/types/plugin'; 3 | export default function createLayout(name: string, components: object, tpl: string): PluginObject { 4 | return { 5 | name, 6 | props: ['title', 'description', 'keywords'], 7 | components, 8 | computed: { 9 | vTitle() { 10 | return this.$root.title || this.title || 'Egg + TypeScript + Element + Webpack'; 11 | }, 12 | vKeywords() { 13 | return this.$root.keywords || this.keywords || 'egg, typescript, vue, webpack, server side render'; 14 | }, 15 | vDescription() { 16 | return this.$root.description || this.description || 'Egg + TypeScript + Element + Webpack server side render'; 17 | }, 18 | baseClass() { 19 | return this.$root.baseClass; 20 | } 21 | }, 22 | template: EASY_ENV_IS_BROWSER ? tpl : ` 23 | 24 | 25 | {{vTitle}} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ${tpl} 44 | 45 | `, 46 | install(vue, options) { 47 | // 48 | } 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /app/web/theme/dialog.css: -------------------------------------------------------------------------------- 1 | .v-modal-enter{-webkit-animation:v-modal-in .2s ease;animation:v-modal-in .2s ease}.v-modal-leave{-webkit-animation:v-modal-out .2s ease forwards;animation:v-modal-out .2s ease forwards}@-webkit-keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-in{0%{opacity:0}}@-webkit-keyframes v-modal-out{100%{opacity:0}}@keyframes v-modal-out{100%{opacity:0}}.v-modal{position:fixed;left:0;top:0;width:100%;height:100%;opacity:.5;background:#000}.el-dialog{position:relative;margin:0 auto 50px;background:#fff;border-radius:2px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.3);box-shadow:0 1px 3px rgba(0,0,0,.3);-webkit-box-sizing:border-box;box-sizing:border-box;width:50%}.el-dialog.is-fullscreen{width:100%;margin-top:0;margin-bottom:0;height:100%;overflow:auto}.el-dialog__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;margin:0}.el-dialog__header{padding:15px 15px 10px}.el-dialog__headerbtn{position:absolute;top:15px;right:15px;padding:0;background:0 0;border:none;outline:0;cursor:pointer;font-size:16px}.el-dialog__headerbtn .el-dialog__close{color:#878d99}.el-dialog__headerbtn:focus .el-dialog__close,.el-dialog__headerbtn:hover .el-dialog__close{color:#3C8DBC}.el-dialog__title{line-height:24px;font-size:18px;color:#2d2f33}.el-dialog__body{padding:30px 20px;color:#5a5e66;line-height:24px;font-size:14px}.el-dialog__footer{padding:10px 15px 15px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.el-dialog--center{text-align:center}.el-dialog--center .el-dialog__header{padding-top:30px}.el-dialog--center .el-dialog__body{text-align:initial;padding:25px 27px 30px}.el-dialog--center .el-dialog__footer{text-align:inherit;padding-bottom:30px}.dialog-fade-enter-active{-webkit-animation:dialog-fade-in .3s;animation:dialog-fade-in .3s}.dialog-fade-leave-active{-webkit-animation:dialog-fade-out .3s;animation:dialog-fade-out .3s}@-webkit-keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes dialog-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes dialog-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # egg-typescript-element-kit 2 | 3 | 基于 Egg + TypeScript + Element + Webpack3 多页面和单页面服务端客户端渲染同构完整工程项目. 4 | 5 | - 前台系统:http://localhost:7001 6 | 7 | ![](https://github.com/hubcarl/egg-typescript-element-kit/blob/master/docs/images/home.png) 8 | 9 | - 后台系统:http://localhost:7001/admin 10 | 11 | ![](https://github.com/hubcarl/egg-typescript-element-kit/blob/master/docs/images/admin.png) 12 | 13 | 14 | ## 1. 项目介绍 15 | 16 | ### 插件版本 17 | 18 | - Egg: ^2.x.x 19 | - Node: Node ^8.x.x+, 20 | - Webpack ^4.x.x 21 | - Vue: ^2.5.0 22 | - TypeScript: ^2.6.2 23 | - Element UI: ^2.0.0 24 | - [easywebpack-vue](https://github.com/hubcarl/easywebpack) 25 | - [egg-view-vue-ssr](https://github.com/hubcarl/egg-view-vue-ssr) 26 | - [egg-webpack](https://github.com/hubcarl/egg-webpack) 27 | - [egg-webpack-vue](https://github.com/hubcarl/egg-webpack-vue) 28 | 29 | ### 项目特性 30 | 31 | - 支持 Egg Node 端代码 和 前端代码 TypeScript 编写和构建 32 | 33 | - 支持 service worker 自动化构建 34 | 35 | - 支持 async 和 await 特性, Controller 采用 class 方式编写 36 | 37 | - 支持 server 和 client 端代码修改, Webpack 时时编译和热更新, `npm run dev` 一键启动应用 38 | 39 | - 基于 vue + vuex + vue-router + axios 单页面服务器客户端同构实现 40 | 41 | - 支持开发环境, 测试环境,正式环境 Webpack 编译 42 | 43 | - 支持 js/css/image 资源依赖, 内置支持CDN特性 44 | 45 | - 支持 Webpack DLL 自动化构建 46 | 47 | - 支持 Vue 组件服务端渲染异步加载 48 | 49 | ## 2. 系统功能 50 | 51 | ### 前台博客系统 52 | 53 | **采用 Egg + Vue 服务端渲染** 54 | 55 | - 博客首页 56 | - 博客文章列表展示 57 | - 博客文章详情页面 58 | - 博客分类浏览 59 | 60 | ### 后台管理系统 61 | 62 | **采用 Egg + Vue + Vue-Router + Element 单页面服务端同构渲染** 63 | 64 | - 用户登陆 65 | - 用户注册 66 | - Dashboard 67 | - 文章管理 68 | - Markdown添加文章 69 | - 权限管理 70 | 71 | ## 3. 使用 72 | 73 | #### 3.1 安装依赖 74 | 75 | ```bash 76 | npm install 77 | npm start 78 | ``` 79 | 80 | #### 3.2 启动应用 81 | 82 | ```bash 83 | npm run dev 84 | ``` 85 | 86 | 应用访问: http://127.0.0.1:7001 87 | 88 | ![npm start启动](https://github.com/hubcarl/egg-typescript-element-kit/blob/master/docs/images/webpack-build.png) 89 | 90 | 91 | #### 3.3 构建 92 | 93 | - TypeScript Egg 构建 94 | 95 | ```bash 96 | npm run tsc 97 | ``` 98 | 99 | - TypeScript 前端工程构建 100 | 101 | ```bash 102 | npm run tsc 103 | ``` 104 | 105 | #### 3.4 打包部署 106 | 107 | 1. 先运行 `npm run build` 构建 TypeScript Egg 代码和 TypeScript 前端代码 108 | 2. 项目代码和构建代码一起打包代码 109 | 3. 应用部署后,通过 `npm start` 启动应用 110 | 111 | ## 4. 文档 112 | 113 | - http://hubcarl.github.io/easywebpack/vue/rule 114 | - https://zhuanlan.zhihu.com/easywebpack 115 | 116 | ## 5. 参考资料 117 | 118 | - [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) 119 | 120 | ## License 121 | 122 | [MIT](LICENSE) 123 | -------------------------------------------------------------------------------- /app/web/component/layout/index/header/header.vue: -------------------------------------------------------------------------------- 1 | 50 | 53 | 71 | -------------------------------------------------------------------------------- /app/web/theme/form.css: -------------------------------------------------------------------------------- 1 | .el-form--inline .el-form-item,.el-form--inline .el-form-item__content{display:inline-block;vertical-align:top}.el-form-item::after,.el-form-item__content::after{clear:both}.el-form--label-left .el-form-item__label{text-align:left}.el-form--label-top .el-form-item__label{float:none;display:inline-block;text-align:left;padding:0 0 10px}.el-form--inline .el-form-item{margin-right:10px}.el-form--inline .el-form-item__label{float:none;display:inline-block}.el-form--inline.el-form--label-top .el-form-item__content{display:block}.el-form-item{margin-bottom:22px}.el-form-item::after,.el-form-item::before{display:table;content:""}.el-form-item .el-form-item{margin-bottom:0}.el-form-item--mini.el-form-item,.el-form-item--small.el-form-item{margin-bottom:18px}.el-form-item .el-input__validateIcon{display:none}.el-form-item--medium .el-form-item__content,.el-form-item--medium .el-form-item__label{line-height:36px}.el-form-item--small .el-form-item__content,.el-form-item--small .el-form-item__label{line-height:32px}.el-form-item--small .el-form-item__error{padding-top:2px}.el-form-item--mini .el-form-item__content,.el-form-item--mini .el-form-item__label{line-height:28px}.el-form-item--mini .el-form-item__error{padding-top:1px}.el-form-item__label{text-align:right;vertical-align:middle;float:left;font-size:14px;color:#5a5e66;line-height:40px;padding:0 12px 0 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-form-item__content{line-height:40px;position:relative;font-size:14px}.el-form-item__content::after,.el-form-item__content::before{display:table;content:""}.el-form-item__error{color:#fa5555;font-size:12px;line-height:1;padding-top:4px;position:absolute;top:100%;left:0}.el-form-item__error--inline{position:relative;top:auto;left:auto;display:inline-block;margin-left:10px}.el-form-item.is-required .el-form-item__label:before{content:'*';color:#fa5555;margin-right:4px}.el-form-item.is-error .el-input__inner,.el-form-item.is-error .el-input__inner:focus,.el-form-item.is-error .el-textarea__inner,.el-form-item.is-error .el-textarea__inner:focus{border-color:#fa5555}.el-form-item.is-error .el-input-group__append .el-input__inner,.el-form-item.is-error .el-input-group__prepend .el-input__inner{border-color:transparent}.el-form-item.is-error .el-input__validateIcon{color:#fa5555}.el-form-item.is-success .el-input__inner,.el-form-item.is-success .el-input__inner:focus,.el-form-item.is-success .el-textarea__inner,.el-form-item.is-success .el-textarea__inner:focus{border-color:#67c23a}.el-form-item.is-success .el-input-group__append .el-input__inner,.el-form-item.is-success .el-input-group__prepend .el-input__inner{border-color:transparent}.el-form-item.is-success .el-input__validateIcon{color:#67c23a}.el-form-item--feedback .el-input__validateIcon{display:inline-block} -------------------------------------------------------------------------------- /app/web/theme/select-dropdown.css: -------------------------------------------------------------------------------- 1 | .el-popper .popper__arrow,.el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.el-popper .popper__arrow::after{content:" ";border-width:6px}.el-popper[x-placement^=top]{margin-bottom:12px}.el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#e6ebf5;border-bottom-width:0}.el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.el-popper[x-placement^=bottom]{margin-top:12px}.el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#e6ebf5}.el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.el-popper[x-placement^=right]{margin-left:12px}.el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#e6ebf5;border-left-width:0}.el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.el-popper[x-placement^=left]{margin-right:12px}.el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#e6ebf5}.el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.el-select-dropdown{position:absolute;z-index:1001;border:1px solid #dfe4ed;border-radius:4px;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:5px 0}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected{color:#3C8DBC;background-color:#fff}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover{background-color:#f5f7fa}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after{position:absolute;right:20px;font-family:element-icons;content:"\E611";font-size:12px;font-weight:700;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-select-dropdown .el-scrollbar.is-empty .el-select-dropdown__list{padding:0}.el-select-dropdown .popper__arrow{-webkit-transform:translateX(-400%);transform:translateX(-400%)}.el-select-dropdown.is-arrow-fixed .popper__arrow{-webkit-transform:translateX(-200%);transform:translateX(-200%)}.el-select-dropdown__empty{padding:10px 0;margin:0;text-align:center;color:#999;font-size:14px}.el-select-dropdown__wrap{max-height:274px}.el-select-dropdown__list{list-style:none;padding:6px 0;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box} -------------------------------------------------------------------------------- /app/web/component/layout/admin/header/header.vue: -------------------------------------------------------------------------------- 1 | 32 | 51 | 94 | -------------------------------------------------------------------------------- /app/web/component/layout/admin/footer/footer.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | height: 120px; 3 | background-color: #324057; 4 | color: #a4aebd; 5 | width: 100%; 6 | } 7 | 8 | .footer * { 9 | word-spacing: 0 10 | } 11 | 12 | .footer .container { 13 | height: 100%; 14 | box-sizing: border-box 15 | } 16 | 17 | .footer .footer-main { 18 | font-size: 0; 19 | padding-top: 40px; 20 | display: inline-block 21 | } 22 | 23 | .footer .footer-main .footer-main-title { 24 | line-height: 1; 25 | font-size: 22px; 26 | margin: 0 27 | } 28 | 29 | .footer .footer-main .footer-main-link { 30 | display: inline-block; 31 | margin: 12px 18px 0 0; 32 | line-height: 1; 33 | font-size: 12px; 34 | color: #768193 35 | } 36 | 37 | .footer .footer-main .footer-main-link a { 38 | color: #768193; 39 | text-decoration: none 40 | } 41 | 42 | .footer .footer-social { 43 | float: right; 44 | line-height: 120px 45 | } 46 | 47 | .footer .footer-social .elementdoc { 48 | transition: .3s; 49 | display: inline-block; 50 | line-height: 32px; 51 | text-align: center; 52 | color: #8d99ab; 53 | background-color: transparent; 54 | width: 32px; 55 | height: 32px; 56 | font-size: 32px; 57 | vertical-align: middle 58 | } 59 | 60 | .footer .footer-social .elementdoc:hover { 61 | transform: scale(1.2) 62 | } 63 | 64 | .footer .footer-social .doc-icon-weixin { 65 | margin-right: 36px 66 | } 67 | 68 | .footer .footer-social .doc-icon-weixin:hover { 69 | color: #fff 70 | } 71 | 72 | .footer .footer-social .doc-icon-github { 73 | margin-right: 0 74 | } 75 | 76 | .footer .footer-social .doc-icon-github:hover { 77 | color: #fff 78 | } 79 | 80 | .footer-popover { 81 | padding: 0; 82 | min-width: 120px; 83 | line-height: normal; 84 | box-shadow: 0 0 11px 0 rgba(174, 187, 211, .24) 85 | } 86 | 87 | .footer-popover .footer-popover-title { 88 | border-bottom: 1px solid #eaeefb; 89 | height: 30px; 90 | line-height: 30px; 91 | text-align: center; 92 | color: #99a9bf; 93 | background-color: #f8f9fe 94 | } 95 | 96 | .footer-popover img { 97 | width: 100px; 98 | height: 100px; 99 | margin: 10px 100 | } 101 | 102 | @media (max-width: 768px) { 103 | .footer .footer-social { 104 | display: none 105 | } 106 | } 107 | 108 | .footer-nav { 109 | padding: 24px 0; 110 | color: #99a9bf; 111 | font-size: 14px 112 | } 113 | 114 | .footer-nav:after { 115 | content: ""; 116 | display: block; 117 | clear: both 118 | } 119 | 120 | .footer-nav i { 121 | transition: .3s; 122 | color: #d9def1; 123 | vertical-align: baseline 124 | } 125 | 126 | .footer-nav-link { 127 | cursor: pointer; 128 | transition: .3s 129 | } 130 | 131 | .footer-nav-link:hover, .footer-nav-link:hover i { 132 | color: #20a0ff 133 | } 134 | 135 | .footer-nav-left { 136 | float: left; 137 | margin-left: -4px 138 | } 139 | 140 | .footer-nav-right { 141 | float: right; 142 | margin-right: -4px 143 | } -------------------------------------------------------------------------------- /app/web/component/MarkdownEditor/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 81 | 82 | 117 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "egg-typescript-element-kit", 3 | "version": "2.0.0", 4 | "description": "基于 egg + typescript + element + webpack 服务端渲染同构工程骨架项目", 5 | "scripts": { 6 | "tsc": "tsc -p tsconfig.json", 7 | "build": "npm run tsc && easy build prod", 8 | "dev": "egg-bin dev -r 'egg-ts-helper/register'", 9 | "start": "egg-scripts start", 10 | "lint": "tslint --project .", 11 | "fix": "tslint --project . --fix", 12 | "debug": "egg-bin debug", 13 | "autod": "egg-bin autod", 14 | "cov": "egg-bin cov", 15 | "test": "npm run lint && egg-bin test", 16 | "ii": "npm install --registry https://registry.npm.taobao.org" 17 | }, 18 | "dependencies": { 19 | "@hubcarl/json-typescript-mapper": "^1.1.4", 20 | "axios": "^0.17.1", 21 | "cross-env": "^5.0.0", 22 | "egg": "^2.1.0", 23 | "egg-bin": "^4.3.7", 24 | "egg-cors": "^2.0.0", 25 | "egg-logger": "^1.5.0", 26 | "egg-scripts": "^2.5.1", 27 | "egg-validate": "^1.0.0", 28 | "egg-view-vue-ssr": "^3.0.5", 29 | "element-ui": "^2.0.8", 30 | "extend": "~3.0.0", 31 | "font-awesome": "^4.7.0", 32 | "lodash": "^4.17.4", 33 | "lodash-id": "^0.14.0", 34 | "lowdb": "^1.0.0", 35 | "mockjs": "^1.0.1-beta3", 36 | "moment": "^2.17.1", 37 | "shortid": "^2.2.8", 38 | "showdown": "^1.8.6", 39 | "simplemde": "^1.11.2", 40 | "vue": "^2.5.0", 41 | "vue-hot-reload-api": "^2.1.0", 42 | "vue-material-input": "^1.2.0", 43 | "vue-router": "^3.0.1", 44 | "vuex": "^3.0.1", 45 | "vuex-router-sync": "^5.0.0" 46 | }, 47 | "devDependencies": { 48 | "autod-egg": "^1.0.0", 49 | "autoprefixer": "^7.1.4", 50 | "babel-plugin-add-module-exports": "^0.2.1", 51 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 52 | "babel-plugin-transform-object-assign": "^6.22.0", 53 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 54 | "babel-plugin-transform-runtime": "^6.15.0", 55 | "babel-preset-env": "^1.6.0", 56 | "cz-conventional-changelog": "^2.1.0", 57 | "easywebpack-cli": "^3.9.0", 58 | "easywebpack-vue": "^4.0.0", 59 | "egg-logview": "^1.0.0", 60 | "egg-ts-helper": "^1.9.2", 61 | "egg-webpack": "^4.0.0", 62 | "egg-webpack-vue": "^2.0.0", 63 | "imagemin-webpack-plugin": "^2.1.0", 64 | "ip": "^1.1.5", 65 | "less": "^2.7.3", 66 | "less-loader": "^4.0.5", 67 | "ts-loader": "^4.0.0", 68 | "ts-node": "^5.0.1", 69 | "tslint": "^5.9.1", 70 | "tslint-loader": "^3.5.3", 71 | "typescript": "^2.6.2" 72 | }, 73 | "egg": { 74 | "typescript": true 75 | }, 76 | "engines": { 77 | "node": ">=8.0.0" 78 | }, 79 | "ci": { 80 | "version": "8, 9" 81 | }, 82 | "repository": { 83 | "type": "git", 84 | "url": "git+https://github.com/hubcarl/egg-typescript-element-kit.git" 85 | }, 86 | "author": "hubcarl@126.com", 87 | "license": "MIT", 88 | "homepage": "https://github.com/hubcarl/egg-typescript-element-kit", 89 | "config": { 90 | "commitizen": { 91 | "path": "./node_modules/cz-conventional-changelog" 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/web/theme/radio.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";.el-radio,.el-radio--medium.is-bordered .el-radio__label{font-size:14px}.el-radio,.el-radio__input{white-space:nowrap;line-height:1;outline:0}.el-radio,.el-radio__inner,.el-radio__input{position:relative;display:inline-block}.el-radio{color:#5a5e66;font-weight:500;cursor:pointer;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.el-radio.is-bordered{padding:10px 20px 10px 10px;border-radius:4px;border:1px solid #d8dce5}.el-radio.is-bordered.is-checked{border-color:#3C8DBC}.el-radio.is-bordered.is-disabled{cursor:not-allowed;border-color:#e6ebf5}.el-radio__input.is-disabled .el-radio__inner,.el-radio__input.is-disabled.is-checked .el-radio__inner{background-color:#f5f7fa;border-color:#dfe4ed}.el-radio.is-bordered+.el-radio.is-bordered{margin-left:10px}.el-radio--medium.is-bordered{padding:8px 20px 8px 10px;border-radius:4px}.el-radio--mini.is-bordered .el-radio__label,.el-radio--small.is-bordered .el-radio__label{font-size:12px}.el-radio--medium.is-bordered .el-radio__inner{height:14px;width:14px}.el-radio--mini.is-bordered .el-radio__inner,.el-radio--small.is-bordered .el-radio__inner{height:12px;width:12px}.el-radio--small.is-bordered{padding:6px 15px 6px 10px;border-radius:3px}.el-radio--mini.is-bordered{padding:4px 15px 4px 10px;border-radius:3px}.el-radio+.el-radio{margin-left:30px}.el-radio__input{cursor:pointer;vertical-align:middle}.el-radio__input.is-disabled .el-radio__inner{cursor:not-allowed}.el-radio__input.is-disabled .el-radio__inner::after{cursor:not-allowed;background-color:#f5f7fa}.el-radio__input.is-disabled .el-radio__inner+.el-radio__label{cursor:not-allowed}.el-radio__input.is-disabled.is-checked .el-radio__inner::after{background-color:#b4bccc}.el-radio__input.is-disabled+span.el-radio__label{color:#b4bccc;cursor:not-allowed}.el-radio__input.is-checked .el-radio__inner{border-color:#3C8DBC;background:#3C8DBC}.el-radio__input.is-checked .el-radio__inner::after{-webkit-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}.el-radio__input.is-checked+.el-radio__label{color:#3C8DBC}.el-radio__input.is-focus .el-radio__inner{border-color:#3C8DBC}.el-radio__inner{border:1px solid #d8dce5;border-radius:100%;width:14px;height:14px;background-color:#fff;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box}.el-radio__inner:hover{border-color:#3C8DBC}.el-radio__inner::after{width:4px;height:4px;border-radius:100%;background-color:#fff;content:"";position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%) scale(0);transform:translate(-50%,-50%) scale(0);-webkit-transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6);transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6);transition:transform .15s cubic-bezier(.71,-.46,.88,.6);transition:transform .15s cubic-bezier(.71,-.46,.88,.6),-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6)}.el-radio__original{opacity:0;outline:0;position:absolute;z-index:-1;top:0;left:0;right:0;bottom:0;margin:0}.el-radio:focus:not(.is-focus):not(:active) .el-radio__inner{-webkit-box-shadow:0 0 2px 2px #3C8DBC;box-shadow:0 0 2px 2px #3C8DBC}.el-radio__label{font-size:14px;padding-left:10px} -------------------------------------------------------------------------------- /app/web/theme/icon.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:element-icons;src:url(fonts/element-icons.woff?t=1508751886602) format("woff"),url(fonts/element-icons.ttf?t=1508751886602) format("truetype");font-weight:400;font-style:normal}[class*=" el-icon-"],[class^=el-icon-]{font-family:element-icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-icon-upload:before{content:"\e60d"}.el-icon-error:before{content:"\e62c"}.el-icon-success:before{content:"\e62d"}.el-icon-warning:before{content:"\e62e"}.el-icon-sort-down:before{content:"\e630"}.el-icon-sort-up:before{content:"\e631"}.el-icon-arrow-left:before{content:"\e600"}.el-icon-circle-plus:before{content:"\e601"}.el-icon-circle-plus-outline:before{content:"\e602"}.el-icon-arrow-down:before{content:"\e603"}.el-icon-arrow-right:before{content:"\e604"}.el-icon-arrow-up:before{content:"\e605"}.el-icon-back:before{content:"\e606"}.el-icon-circle-close:before{content:"\e607"}.el-icon-date:before{content:"\e608"}.el-icon-circle-close-outline:before{content:"\e609"}.el-icon-caret-left:before{content:"\e60a"}.el-icon-caret-bottom:before{content:"\e60b"}.el-icon-caret-top:before{content:"\e60c"}.el-icon-caret-right:before{content:"\e60e"}.el-icon-close:before{content:"\e60f"}.el-icon-d-arrow-left:before{content:"\e610"}.el-icon-check:before{content:"\e611"}.el-icon-delete:before{content:"\e612"}.el-icon-d-arrow-right:before{content:"\e613"}.el-icon-document:before{content:"\e614"}.el-icon-d-caret:before{content:"\e615"}.el-icon-edit-outline:before{content:"\e616"}.el-icon-download:before{content:"\e617"}.el-icon-goods:before{content:"\e618"}.el-icon-search:before{content:"\e619"}.el-icon-info:before{content:"\e61a"}.el-icon-message:before{content:"\e61b"}.el-icon-edit:before{content:"\e61c"}.el-icon-location:before{content:"\e61d"}.el-icon-loading:before{content:"\e61e"}.el-icon-location-outline:before{content:"\e61f"}.el-icon-menu:before{content:"\e620"}.el-icon-minus:before{content:"\e621"}.el-icon-bell:before{content:"\e622"}.el-icon-mobile-phone:before{content:"\e624"}.el-icon-news:before{content:"\e625"}.el-icon-more:before{content:"\e646"}.el-icon-more-outline:before{content:"\e626"}.el-icon-phone:before{content:"\e627"}.el-icon-phone-outline:before{content:"\e628"}.el-icon-picture:before{content:"\e629"}.el-icon-picture-outline:before{content:"\e62a"}.el-icon-plus:before{content:"\e62b"}.el-icon-printer:before{content:"\e62f"}.el-icon-rank:before{content:"\e632"}.el-icon-refresh:before{content:"\e633"}.el-icon-question:before{content:"\e634"}.el-icon-remove:before{content:"\e635"}.el-icon-share:before{content:"\e636"}.el-icon-star-on:before{content:"\e637"}.el-icon-setting:before{content:"\e638"}.el-icon-circle-check:before{content:"\e639"}.el-icon-service:before{content:"\e63a"}.el-icon-sold-out:before{content:"\e63b"}.el-icon-remove-outline:before{content:"\e63c"}.el-icon-star-off:before{content:"\e63d"}.el-icon-circle-check-outline:before{content:"\e63e"}.el-icon-tickets:before{content:"\e63f"}.el-icon-sort:before{content:"\e640"}.el-icon-zoom-in:before{content:"\e641"}.el-icon-time:before{content:"\e642"}.el-icon-view:before{content:"\e643"}.el-icon-upload2:before{content:"\e644"}.el-icon-zoom-out:before{content:"\e645"}.el-icon-loading{-webkit-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}.el-icon--right{margin-left:5px}.el-icon--left{margin-right:5px}@-webkit-keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}}@keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}} -------------------------------------------------------------------------------- /app/web/component/layout/admin/content/content.css: -------------------------------------------------------------------------------- 1 | .main{ 2 | margin-top: -80px; 3 | padding: 80px 0 120px; 4 | box-sizing: border-box; 5 | } 6 | .page-container{ 7 | width: 1140px; 8 | padding: 0 30px; 9 | margin: 36 auto; 10 | } 11 | .page-component { 12 | padding-bottom: 95px; 13 | box-sizing: border-box 14 | } 15 | 16 | .page-component .content { 17 | margin-left: -1px 18 | } 19 | 20 | .page-component .content > h3 { 21 | margin: 45px 0 15px 22 | } 23 | 24 | .page-component .content > table { 25 | border-collapse: collapse; 26 | width: 100%; 27 | background-color: #fff; 28 | color: #5e6d82; 29 | font-size: 14px; 30 | margin-bottom: 45px 31 | } 32 | 33 | .page-component .content > table strong { 34 | font-weight: 400 35 | } 36 | 37 | .page-component .content > table th { 38 | text-align: left; 39 | border-top: 1px solid #eaeefb; 40 | background-color: #eff2f7; 41 | white-space: nowrap 42 | } 43 | 44 | .page-component .content > table td, .page-component .content > table th { 45 | border-bottom: 1px solid #eaeefb; 46 | padding: 10px; 47 | max-width: 250px 48 | } 49 | 50 | .page-component .content > table td:first-child, .page-component .content > table th:first-child { 51 | padding-left: 10px 52 | } 53 | 54 | .page-component .page-component-up { 55 | background-color: #58b7ff; 56 | position: fixed; 57 | right: 100px; 58 | bottom: 150px; 59 | width: 50px; 60 | height: 50px; 61 | border-radius: 25px; 62 | cursor: pointer; 63 | opacity: .4; 64 | transition: .3s 65 | } 66 | 67 | .page-component .page-component-up i { 68 | color: #fff; 69 | display: block; 70 | line-height: 50px; 71 | text-align: center; 72 | font-size: 22px 73 | } 74 | 75 | .page-component .page-component-up.hover { 76 | opacity: 1 77 | } 78 | 79 | .page-component .back-top-fade-enter, .page-component .back-top-fade-leave-active { 80 | transform: translateY(-30px); 81 | opacity: 0 82 | } 83 | 84 | .page-component { 85 | padding-bottom: 95px; 86 | box-sizing: border-box 87 | } 88 | 89 | .page-component .content { 90 | margin-left: -1px 91 | } 92 | 93 | .page-component .content > h3 { 94 | margin: 45px 0 15px 95 | } 96 | 97 | .page-component .content > table { 98 | border-collapse: collapse; 99 | width: 100%; 100 | background-color: #fff; 101 | color: #5e6d82; 102 | font-size: 14px; 103 | margin-bottom: 45px 104 | } 105 | 106 | .page-component .content > table strong { 107 | font-weight: 400 108 | } 109 | 110 | .page-component .content > table th { 111 | text-align: left; 112 | border-top: 1px solid #eaeefb; 113 | background-color: #eff2f7; 114 | white-space: nowrap 115 | } 116 | 117 | .page-component .content > table td, .page-component .content > table th { 118 | border-bottom: 1px solid #eaeefb; 119 | padding: 10px; 120 | max-width: 250px 121 | } 122 | 123 | .page-component .content > table td:first-child, .page-component .content > table th:first-child { 124 | padding-left: 10px 125 | } 126 | 127 | .page-component .page-component-up { 128 | background-color: #58b7ff; 129 | position: fixed; 130 | right: 100px; 131 | bottom: 150px; 132 | width: 50px; 133 | height: 50px; 134 | border-radius: 25px; 135 | cursor: pointer; 136 | opacity: .4; 137 | transition: .3s 138 | } 139 | 140 | .page-component .page-component-up i { 141 | color: #fff; 142 | display: block; 143 | line-height: 50px; 144 | text-align: center; 145 | font-size: 22px 146 | } 147 | 148 | .page-component .page-component-up.hover { 149 | opacity: 1 150 | } 151 | 152 | .page-component .back-top-fade-enter, .page-component .back-top-fade-leave-active { 153 | transform: translateY(-30px); 154 | opacity: 0 155 | } -------------------------------------------------------------------------------- /app/web/component/layout/index/content/content.css: -------------------------------------------------------------------------------- 1 | .main{ 2 | margin-top: -80px; 3 | padding: 80px 0 120px; 4 | box-sizing: border-box; 5 | } 6 | .page-container{ 7 | width: 1140px; 8 | padding: 0 30px; 9 | margin: 36 auto; 10 | } 11 | .page-component { 12 | padding-bottom: 95px; 13 | box-sizing: border-box 14 | } 15 | 16 | .page-component .content { 17 | margin-left: -1px 18 | } 19 | 20 | .page-component .content > h3 { 21 | margin: 45px 0 15px 22 | } 23 | 24 | .page-component .content > table { 25 | border-collapse: collapse; 26 | width: 100%; 27 | background-color: #fff; 28 | color: #5e6d82; 29 | font-size: 14px; 30 | margin-bottom: 45px 31 | } 32 | 33 | .page-component .content > table strong { 34 | font-weight: 400 35 | } 36 | 37 | .page-component .content > table th { 38 | text-align: left; 39 | border-top: 1px solid #eaeefb; 40 | background-color: #eff2f7; 41 | white-space: nowrap 42 | } 43 | 44 | .page-component .content > table td, .page-component .content > table th { 45 | border-bottom: 1px solid #eaeefb; 46 | padding: 10px; 47 | max-width: 250px 48 | } 49 | 50 | .page-component .content > table td:first-child, .page-component .content > table th:first-child { 51 | padding-left: 10px 52 | } 53 | 54 | .page-component .page-component-up { 55 | background-color: #58b7ff; 56 | position: fixed; 57 | right: 100px; 58 | bottom: 150px; 59 | width: 50px; 60 | height: 50px; 61 | border-radius: 25px; 62 | cursor: pointer; 63 | opacity: .4; 64 | transition: .3s 65 | } 66 | 67 | .page-component .page-component-up i { 68 | color: #fff; 69 | display: block; 70 | line-height: 50px; 71 | text-align: center; 72 | font-size: 22px 73 | } 74 | 75 | .page-component .page-component-up.hover { 76 | opacity: 1 77 | } 78 | 79 | .page-component .back-top-fade-enter, .page-component .back-top-fade-leave-active { 80 | transform: translateY(-30px); 81 | opacity: 0 82 | } 83 | 84 | .page-component { 85 | padding-bottom: 95px; 86 | box-sizing: border-box 87 | } 88 | 89 | .page-component .content { 90 | margin-left: -1px 91 | } 92 | 93 | .page-component .content > h3 { 94 | margin: 45px 0 15px 95 | } 96 | 97 | .page-component .content > table { 98 | border-collapse: collapse; 99 | width: 100%; 100 | background-color: #fff; 101 | color: #5e6d82; 102 | font-size: 14px; 103 | margin-bottom: 45px 104 | } 105 | 106 | .page-component .content > table strong { 107 | font-weight: 400 108 | } 109 | 110 | .page-component .content > table th { 111 | text-align: left; 112 | border-top: 1px solid #eaeefb; 113 | background-color: #eff2f7; 114 | white-space: nowrap 115 | } 116 | 117 | .page-component .content > table td, .page-component .content > table th { 118 | border-bottom: 1px solid #eaeefb; 119 | padding: 10px; 120 | max-width: 250px 121 | } 122 | 123 | .page-component .content > table td:first-child, .page-component .content > table th:first-child { 124 | padding-left: 10px 125 | } 126 | 127 | .page-component .page-component-up { 128 | background-color: #58b7ff; 129 | position: fixed; 130 | right: 100px; 131 | bottom: 150px; 132 | width: 50px; 133 | height: 50px; 134 | border-radius: 25px; 135 | cursor: pointer; 136 | opacity: .4; 137 | transition: .3s 138 | } 139 | 140 | .page-component .page-component-up i { 141 | color: #fff; 142 | display: block; 143 | line-height: 50px; 144 | text-align: center; 145 | font-size: 22px 146 | } 147 | 148 | .page-component .page-component-up.hover { 149 | opacity: 1 150 | } 151 | 152 | .page-component .back-top-fade-enter, .page-component .back-top-fade-leave-active { 153 | transform: translateY(-30px); 154 | opacity: 0 155 | } -------------------------------------------------------------------------------- /app/mocks/article/list.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const data = { 3 | list: [ { 4 | id: 1, 5 | title: 'vue-渐进式JavaScript 框架', 6 | summary: '简单小巧的核心,渐进式技术栈,足以应付任何规模的应用', 7 | hits: 200, 8 | url: 'https://cn.vuejs.org', 9 | status: 1, 10 | }, { 11 | id: 2, 12 | title: 'webpack配置官方文档', 13 | summary: 'webpack is a module bundler for modern JavaScript applications.', 14 | hits: 550, 15 | url: 'https://webpack.js.org/configuration/', 16 | status: 1, 17 | }, { 18 | id: 3, 19 | title: 'egg-为企业级框架和应用而生', 20 | summary: 'Born to buildbetter enterprise frameworks and apps with Node.js & Koa', 21 | hits: 278, 22 | url: 'https://eggjs.org/', 23 | }, { 24 | id: 4, 25 | title: 'axios-基于 Promise 的 HTTP 请求客户端', 26 | summary: '基于 Promise 的 HTTP 请求客户端,可同时在浏览器和 node.js 中使用', 27 | hits: 998, 28 | url: 'https://www.awesomes.cn/repo/mzabriskie/axios', 29 | }, { 30 | id: 4, 31 | title: 'Centralized State Management for Vue.js', 32 | summary: 'Vuex 是一个专为Vue.js 应用程序开发的状态管理模式', 33 | hits: 232, 34 | url: 'https://github.com/vuejs/vuex', 35 | }, { 36 | id: 4, 37 | title: 'vue服务器渲染', 38 | summary: '服务器渲染可以加快首屏速度,利于SEO', 39 | hits: 565, 40 | url: 'http://csbun.github.io/blog/2016/08/vue-2-0-server-side-rendering/', 41 | }, { 42 | id: 4, 43 | title: 'webpack服务器构建', 44 | summary: 'Webpack is an amazing tool.', 45 | hits: 988, 46 | url: 'http://jlongster.com/Backend-Apps-with-Webpack--Part-I', 47 | }, { 48 | id: 4, 49 | title: 'vue component loader for Webpack', 50 | summary: 'Webpack loader for Vue.js components', 51 | hits: 322, 52 | url: 'https://github.com/vuejs/vue-loader', 53 | }, { 54 | id: 4, 55 | title: 'vue-router--The official router for Vue.js', 56 | summary: 'It deeply integrates with Vue.js core to make building Single Page Applications with Vue.js a breeze', 57 | hits: 566, 58 | url: 'https://github.com/vuejs/vue-router', 59 | }, { 60 | id: 4, 61 | title: 'vue生命周期', 62 | summary: 'Vue.js 生命周期和route的生命周期讲解', 63 | hits: 434, 64 | url: 'http://www.jianshu.com/p/e9f884b6ba6c', 65 | }, { 66 | id: 4, 67 | title: 'babel到底将代码转换成什么鸟样', 68 | summary: '将babel捧作前端一个划时代的工具一定也不为过,它的出现让许多程序员幸福地用上了es6新语法', 69 | hits: 432, 70 | url: 'https://github.com/lcxfs1991/blog/issues/9', 71 | }, { 72 | id: 4, 73 | title: 'HTTP2 的真正性能到底如何', 74 | summary: 'HTTP2 的真正性能到底如何', 75 | hits: 565, 76 | url: 'https://segmentfault.com/a/1190000007219256?utm_source=weekly&utm_medium=email&utm_campaign=email_weekly', 77 | }, { 78 | id: 4, 79 | title: 'HTTP,HTTP2.0,SPDY,HTTPS讲解', 80 | summary: '使用SPDY加快你的网站速度', 81 | hits: 787, 82 | url: 'http://www.alloyteam.com/2016/07/httphttp2-0spdyhttps-reading-this-is-enough/', 83 | }, { 84 | id: 4, 85 | title: 'git - 简明指南', 86 | summary: '助你入门 git 的简明指南', 87 | hits: 121, 88 | url: 'http://rogerdudler.github.io/git-guide/index.zh.html', 89 | }, { 90 | id: 4, 91 | title: 'vue从1升级到2', 92 | summary: 'Migrating from v1 to v2', 93 | hits: 555, 94 | url: 'https://webpack.js.org/guides/migrating/', 95 | }], 96 | }; 97 | 98 | let id: number = 1; 99 | 100 | const deepCopy = (json) => { 101 | return JSON.parse(JSON.stringify(json)); 102 | }; 103 | 104 | data.list = data.list.concat(deepCopy(data.list)); 105 | data.list = data.list.concat(deepCopy(data.list)); 106 | 107 | data.list.forEach((item) => { 108 | item.id = id++; 109 | }); 110 | 111 | const total = data.list.length; 112 | export function getPage(pageIndex: number = 1, pageSize: number = 10) { 113 | const start = (pageIndex - 1) * pageSize; 114 | const end = start + Number(pageSize); 115 | const list = data.list.slice(start, end); 116 | return { list, total }; 117 | } 118 | 119 | export function getDetail(uniqueId: number) { 120 | return data.list.filter((item) => { 121 | return item.id === uniqueId; 122 | }).slice(0, 1); 123 | } -------------------------------------------------------------------------------- /app/web/page/admin/home/component/panel.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 60 | 61 | 149 | -------------------------------------------------------------------------------- /config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ 5 | // "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | // "lib": [], /* Specify library files to be included in the compilation: */ 7 | "allowJs": true, /* Allow javascript files to be compiled. */ 8 | // "checkJs": true, /* Report errors in .js files. */ 9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 11 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 12 | // "outFile": "./", /* Concatenate and emit output to single file. */ 13 | // "outDir": "./", /* Redirect output structure to the directory. */ 14 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 15 | // "removeComments": true, /* Do not emit comments to output. */ 16 | // "noEmit": true, /* Do not emit outputs. */ 17 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 18 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 19 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 20 | 21 | /* Strict Type-Checking Options */ 22 | "strict": false, /* Enable all strict type-checking options. */ 23 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 24 | // "strictNullChecks": true, /* Enable strict null checks. */ 25 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 26 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 27 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 28 | 29 | /* Additional Checks */ 30 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 31 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 32 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 33 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 34 | 35 | /* Module Resolution Options */ 36 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 37 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 38 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 39 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 40 | // "typeRoots": [], /* List of folders to include type definitions from. */ 41 | // "types": [], /* Type declaration files to be included in compilation. */ 42 | "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 43 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 44 | 45 | /* Source Map Options */ 46 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 47 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ 48 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 49 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 50 | } 51 | } -------------------------------------------------------------------------------- /app/web/theme/menu.css: -------------------------------------------------------------------------------- 1 | .el-menu,.el-menu li{list-style:none}.el-menu,.el-menu--horizontal .el-menu-item:focus,.el-menu--horizontal .el-menu-item:hover,.el-menu--horizontal .el-submenu .el-submenu__title:hover,.el-menu--horizontal .el-submenu>.el-menu{background-color:#fff}.el-menu{border-right:solid 1px #e6e6e6;position:relative;margin:0;padding-left:0}.el-menu::after,.el-menu::before{display:table;content:""}.el-menu::after{clear:both}.el-menu--horizontal{border-right:none;border-bottom:solid 1px #e6e6e6}.el-menu--horizontal .el-menu-item{float:left;height:60px;line-height:60px;margin:0;cursor:pointer;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;border-bottom:2px solid transparent;color:#878d99}.el-menu--horizontal .el-menu-item a,.el-menu--horizontal .el-menu-item a:hover{color:inherit}.el-menu--horizontal .el-submenu{float:left;position:relative}.el-menu--horizontal .el-submenu:focus{outline:0}.el-menu--horizontal .el-submenu:focus>.el-submenu__title{color:#2d2f33}.el-menu--horizontal .el-submenu>.el-menu{position:absolute;top:65px;left:0;border:none;padding:5px 0;z-index:100;min-width:100%;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:2px}.el-menu--horizontal .el-submenu .el-submenu__title{height:60px;line-height:60px;border-bottom:2px solid transparent;color:#878d99}.el-menu--horizontal .el-submenu .el-menu-item{background-color:#fff;float:none;height:36px;line-height:36px;padding:0 10px}.el-menu--horizontal .el-submenu .el-submenu__icon-arrow{position:static;vertical-align:middle;margin-left:8px;margin-top:-3px}.el-menu--horizontal .el-menu-item:focus,.el-menu--horizontal .el-menu-item:hover,.el-menu--horizontal .el-submenu__title:hover{outline:0;color:#2d2f33}.el-menu--horizontal>.el-menu-item.is-active,.el-menu--horizontal>.el-submenu.is-active .el-submenu__title{border-bottom:2px solid #3C8DBC;color:#2d2f33}.el-menu--collapse{width:64px}.el-menu--collapse>.el-menu-item [class^=el-icon-],.el-menu--collapse>.el-submenu>.el-submenu__title [class^=el-icon-]{margin:0;vertical-align:middle;width:24px;text-align:center}.el-menu--collapse>.el-menu-item .el-submenu__icon-arrow,.el-menu--collapse>.el-submenu>.el-submenu__title .el-submenu__icon-arrow{display:none}.el-menu--collapse>.el-menu-item span,.el-menu--collapse>.el-submenu>.el-submenu__title span{height:0;width:0;overflow:hidden;visibility:hidden;display:inline-block}.el-menu--collapse>.el-menu-item.is-active i{color:inherit}.el-menu--collapse .el-menu .el-submenu{min-width:200px}.el-menu--collapse .el-submenu{position:relative}.el-menu--collapse .el-submenu .el-menu{position:absolute;margin-left:5px;top:0;left:100%;z-index:10;border:1px solid #dfe4ed;border-radius:2px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-menu-item,.el-submenu__title{height:56px;line-height:56px;padding:0 20px;cursor:pointer;position:relative;white-space:nowrap}.el-menu--collapse .el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:none;transform:none}.el-menu-item{font-size:14px;color:#2d2f33;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;-webkit-box-sizing:border-box;box-sizing:border-box}.el-menu-item [class^=el-icon-]{margin-right:5px;width:24px;text-align:center;font-size:18px}.el-menu-item *{vertical-align:middle}.el-menu-item:first-child{margin-left:0}.el-menu-item:last-child{margin-right:0}.el-menu-item:focus,.el-menu-item:hover{outline:0;background-color:rgb(236, 244, 248)}.el-menu-item i{color:#878d99}.el-menu-item.is-active{color:#3C8DBC}.el-menu-item.is-active i{color:inherit}.el-submenu__title{font-size:14px;color:#2d2f33;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;-webkit-box-sizing:border-box;box-sizing:border-box}.el-submenu__title *{vertical-align:middle}.el-submenu__title i{color:#878d99}.el-submenu__title:hover{background-color:rgb(236, 244, 248)}.el-submenu .el-menu{border:none}.el-submenu .el-menu-item{height:50px;line-height:50px;padding:0 45px;min-width:200px}.el-submenu__icon-arrow{position:absolute;top:50%;right:20px;margin-top:-7px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-size:12px}.el-submenu.is-active .el-submenu__title{border-bottom-color:#3C8DBC}.el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg)}.el-submenu [class^=el-icon-]{vertical-align:middle;margin-right:5px;width:24px;text-align:center;font-size:18px}.el-menu-item-group>ul{padding:0}.el-menu-item-group__title{padding:7px 0 7px 20px;line-height:normal;font-size:12px;color:#878d99}.horizontal-collapse-transition .el-submenu__title .el-submenu__icon-arrow{-webkit-transition:.2s;transition:.2s;opacity:0} -------------------------------------------------------------------------------- /app/web/theme/step.css: -------------------------------------------------------------------------------- 1 | .el-step{position:relative;-ms-flex-negative:1;flex-shrink:1}.el-step:last-of-type .el-step__line{display:none}.el-step:last-of-type.is-flex{-ms-flex-preferred-size:auto!important;flex-basis:auto!important;-ms-flex-negative:0;flex-shrink:0;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0}.el-step:last-of-type .el-step__description,.el-step:last-of-type .el-step__main{padding-right:0}.el-step__head{position:relative;width:100%}.el-step__head.is-process{color:#2d2f33;border-color:#2d2f33}.el-step__head.is-wait{color:#b4bccc;border-color:#b4bccc}.el-step__head.is-success{color:#67c23a;border-color:#67c23a}.el-step__head.is-error{color:#fa5555;border-color:#fa5555}.el-step__head.is-finish{color:#3C8DBC;border-color:#3C8DBC}.el-step__icon{position:relative;z-index:1;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:24px;height:24px;font-size:14px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#fff;-webkit-transition:.15s ease-out;transition:.15s ease-out}.el-step__icon.is-text{border-radius:50%;border:2px solid;border-color:inherit}.el-step__icon.is-icon{width:40px}.el-step__icon-inner{display:inline-block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-align:center;font-weight:700;line-height:1;color:inherit}.el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:25px;font-weight:400}.el-step__icon-inner.is-status{-webkit-transform:translateY(1px);transform:translateY(1px)}.el-step__line{position:absolute;border-color:inherit;background-color:#b4bccc}.el-step__line-inner{display:block;border-width:1px;border-style:solid;border-color:inherit;-webkit-transition:.15s ease-out;transition:.15s ease-out;-webkit-box-sizing:border-box;box-sizing:border-box;width:0;height:0}.el-step__main{white-space:normal;text-align:left}.el-step__title{font-size:16px;line-height:38px}.el-step__title.is-process{font-weight:700;color:#2d2f33}.el-step__title.is-wait{color:#b4bccc}.el-step__title.is-success{color:#67c23a}.el-step__title.is-error{color:#fa5555}.el-step__title.is-finish{color:#3C8DBC}.el-step__description{padding-right:10%;margin-top:-5px;font-size:12px;line-height:20px;font-weight:400}.el-step__description.is-process{color:#2d2f33}.el-step__description.is-wait{color:#b4bccc}.el-step__description.is-success{color:#67c23a}.el-step__description.is-error{color:#fa5555}.el-step__description.is-finish{color:#3C8DBC}.el-step.is-horizontal{display:inline-block}.el-step.is-horizontal .el-step__line{height:2px;top:11px;left:0;right:0}.el-step.is-vertical{display:-webkit-box;display:-ms-flexbox;display:flex}.el-step.is-vertical .el-step__head{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;width:24px}.el-step.is-vertical .el-step__main{padding-left:10px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.el-step.is-vertical .el-step__title{line-height:24px;padding-bottom:8px}.el-step.is-vertical .el-step__line{width:2px;top:0;bottom:0;left:11px}.el-step.is-vertical .el-step__icon.is-icon{width:24px}.el-step.is-center .el-step__head,.el-step.is-center .el-step__main{text-align:center}.el-step.is-center .el-step__description{padding-left:20%;padding-right:20%}.el-step.is-center .el-step__line{left:50%;right:-50%}.el-step.is-simple{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-step.is-simple .el-step__head{width:auto;font-size:0;padding-right:10px}.el-step.is-simple .el-step__icon{background:0 0;width:16px;height:16px;font-size:12px}.el-step.is-simple .el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:18px}.el-step.is-simple .el-step__icon-inner.is-status{-webkit-transform:scale(.8) translateY(1px);transform:scale(.8) translateY(1px)}.el-step.is-simple .el-step__main{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.el-step.is-simple .el-step__title{font-size:16px;line-height:20px}.el-step.is-simple:not(:last-of-type) .el-step__title{max-width:50%;word-break:break-all}.el-step.is-simple .el-step__arrow{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-step.is-simple .el-step__arrow::after,.el-step.is-simple .el-step__arrow::before{content:'';display:inline-block;position:absolute;height:15px;width:1px;background:#b4bccc}.el-step.is-simple .el-step__arrow::before{-webkit-transform:rotate(-45deg) translateY(-4px);transform:rotate(-45deg) translateY(-4px);-webkit-transform-origin:0 0;transform-origin:0 0}.el-step.is-simple .el-step__arrow::after{-webkit-transform:rotate(45deg) translateY(4px);transform:rotate(45deg) translateY(4px);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}.el-step.is-simple:last-of-type .el-step__arrow{display:none} -------------------------------------------------------------------------------- /app/web/theme/input.css: -------------------------------------------------------------------------------- 1 | .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.el-input::-webkit-scrollbar{z-index:11;width:6px}.el-input::-webkit-scrollbar:horizontal{height:6px}.el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.el-input::-webkit-scrollbar-corner{background:#fff}.el-input::-webkit-scrollbar-track{background:#fff}.el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.el-input__prefix,.el-input__suffix{position:absolute;top:0;-webkit-transition:all .3s;text-align:center;height:100%;color:#b4bccc}.el-input__inner::-webkit-input-placeholder{color:#b4bccc}.el-input__inner:-ms-input-placeholder{color:#b4bccc}.el-input__inner::placeholder{color:#b4bccc}.el-input__inner:hover{border-color:#b4bccc}.el-input.is-active .el-input__inner,.el-input__inner:focus{border-color:#3C8DBC;outline:0}.el-input__suffix{right:5px;transition:all .3s;pointer-events:none}.el-input__suffix-inner{pointer-events:all}.el-input__prefix{left:5px;transition:all .3s}.el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.el-input__validateIcon{pointer-events:none}.el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.el-input.is-disabled .el-input__icon{cursor:not-allowed}.el-input--suffix .el-input__inner{padding-right:30px}.el-input--prefix .el-input__inner{padding-left:30px}.el-input--medium{font-size:14px}.el-input--medium .el-input__inner{height:36px}.el-input--medium .el-input__icon{line-height:36px}.el-input--small{font-size:13px}.el-input--small .el-input__inner{height:32px}.el-input--small .el-input__icon{line-height:32px}.el-input--mini{font-size:12px}.el-input--mini .el-input__inner{height:28px}.el-input--mini .el-input__icon{line-height:28px}.el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.el-input-group__append,.el-input-group__prepend{background-color:#f5f7fa;color:#878d99;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.el-input-group--prepend .el-input__inner,.el-input-group__append{border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--append .el-input__inner,.el-input-group__prepend{border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group__append:focus,.el-input-group__prepend:focus{outline:0}.el-input-group__append .el-button,.el-input-group__append .el-select,.el-input-group__prepend .el-button,.el-input-group__prepend .el-select{display:inline-block;margin:-20px}.el-input-group__append button.el-button,.el-input-group__append div.el-select .el-input__inner,.el-input-group__append div.el-select:hover .el-input__inner,.el-input-group__prepend button.el-button,.el-input-group__prepend div.el-select .el-input__inner,.el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.el-input-group__append .el-button,.el-input-group__append .el-input,.el-input-group__prepend .el-button,.el-input-group__prepend .el-input{font-size:inherit}.el-input-group__prepend{border-right:0}.el-input-group__append{border-left:0}.el-textarea{display:inline-block;width:100%;vertical-align:bottom}.el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.el-textarea__inner::placeholder{color:#b4bccc}.el-textarea__inner:hover{border-color:#b4bccc}.el-textarea__inner:focus{outline:0;border-color:#3C8DBC}.el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc} -------------------------------------------------------------------------------- /app/web/theme/color-picker.css: -------------------------------------------------------------------------------- 1 | .el-color-hue-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background-color:red;padding:0 2px}.el-color-hue-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,from(red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%);height:100%}.el-color-hue-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-hue-slider.is-vertical{width:12px;height:180px;padding:2px 0}.el-color-hue-slider.is-vertical .el-color-hue-slider__bar{background:-webkit-gradient(linear,left top,left bottom,from(red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(to bottom,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}.el-color-hue-slider.is-vertical .el-color-hue-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-svpanel{position:relative;width:280px;height:180px}.el-color-svpanel__black,.el-color-svpanel__white{position:absolute;top:0;left:0;right:0;bottom:0}.el-color-svpanel__white{background:-webkit-gradient(linear,left top,right top,from(#fff),to(rgba(255,255,255,0)));background:linear-gradient(to right,#fff,rgba(255,255,255,0))}.el-color-svpanel__black{background:-webkit-gradient(linear,left bottom,left top,from(#000),to(transparent));background:linear-gradient(to top,#000,transparent)}.el-color-svpanel__cursor{position:absolute}.el-color-svpanel__cursor>div{cursor:head;width:4px;height:4px;-webkit-box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);border-radius:50%;-webkit-transform:translate(-2px,-2px);transform:translate(-2px,-2px)}.el-color-alpha-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-alpha-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,from(rgba(255,255,255,0)),to(white));background:linear-gradient(to right,rgba(255,255,255,0) 0,#fff 100%);height:100%}.el-color-alpha-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-alpha-slider.is-vertical{width:20px;height:180px}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__bar{background:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,0)),to(white));background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fff 100%)}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-dropdown{width:300px}.el-color-dropdown__main-wrapper{margin-bottom:6px}.el-color-dropdown__main-wrapper::after{content:"";display:table;clear:both}.el-color-dropdown__btns{margin-top:6px;text-align:right}.el-color-dropdown__value{float:left;line-height:26px;font-size:12px;color:#000;width:160px}.el-color-dropdown__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.el-color-dropdown__btn[disabled]{color:#ccc;cursor:not-allowed}.el-color-dropdown__btn:hover{color:#3C8DBC;border-color:#3C8DBC}.el-color-dropdown__link-btn{cursor:pointer;color:#3C8DBC;text-decoration:none;padding:15px;font-size:12px}.el-color-dropdown__link-btn:hover{color:tint(primary,20%)}.el-color-picker{display:inline-block;position:relative;line-height:normal;height:40px}.el-color-picker.is-disabled .el-color-picker__trigger{cursor:not-allowed}.el-color-picker--medium{height:36px}.el-color-picker--medium .el-color-picker__trigger{height:36px;width:36px}.el-color-picker--medium .el-color-picker__mask{height:34px;width:34px}.el-color-picker--small{height:32px}.el-color-picker--small .el-color-picker__trigger{height:32px;width:32px}.el-color-picker--small .el-color-picker__mask{height:30px;width:30px}.el-color-picker--small .el-color-picker__empty,.el-color-picker--small .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker--mini{height:28px}.el-color-picker--mini .el-color-picker__trigger{height:28px;width:28px}.el-color-picker--mini .el-color-picker__mask{height:26px;width:26px}.el-color-picker--mini .el-color-picker__empty,.el-color-picker--mini .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker__mask{height:38px;width:38px;border-radius:4px;position:absolute;top:1px;left:1px;z-index:1;cursor:not-allowed;background-color:rgba(255,255,255,.7)}.el-color-picker__trigger{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;height:40px;width:40px;padding:4px;border:1px solid #e6e6e6;border-radius:4px;font-size:0;position:relative;cursor:pointer}.el-color-picker__color{position:relative;display:block;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #999;border-radius:2px;width:100%;height:100%;text-align:center}.el-color-picker__color.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-picker__color-inner{position:absolute;left:0;top:0;right:0;bottom:0}.el-color-picker__empty,.el-color-picker__icon{top:50%;left:50%;font-size:12px;position:absolute}.el-color-picker__empty{color:#999;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}.el-color-picker__icon{display:inline-block;width:100%;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0);color:#fff;text-align:center}.el-color-picker__panel{position:absolute;z-index:10;padding:6px;background-color:#fff;border:1px solid #e6ebf5;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)} -------------------------------------------------------------------------------- /app/web/page/admin/home/view/list.vue: -------------------------------------------------------------------------------- 1 | 97 | 100 | 196 | -------------------------------------------------------------------------------- /app/web/theme/checkbox.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";.el-checkbox,.el-checkbox__input{display:inline-block;position:relative}.el-checkbox-button__inner,.el-checkbox__input{white-space:nowrap;vertical-align:middle;outline:0}.el-checkbox{color:#5a5e66;font-weight:500;font-size:14px;cursor:pointer;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.el-checkbox.is-bordered{padding:9px 20px 9px 10px;border-radius:4px;border:1px solid #d8dce5}.el-checkbox.is-bordered.is-checked{border-color:#3C8DBC}.el-checkbox.is-bordered.is-disabled{border-color:#e6ebf5;cursor:not-allowed}.el-checkbox.is-bordered+.el-checkbox.is-bordered{margin-left:10px}.el-checkbox.is-bordered.el-checkbox--medium{padding:7px 20px 7px 10px;border-radius:4px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__label{line-height:17px;font-size:14px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__inner{height:14px;width:14px}.el-checkbox.is-bordered.el-checkbox--small{padding:3px 15px 7px 10px;border-radius:3px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{line-height:15px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner::after{height:6px;width:2px}.el-checkbox.is-bordered.el-checkbox--mini{padding:1px 15px 5px 10px;border-radius:3px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__label{line-height:12px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner::after{height:6px;width:2px}.el-checkbox__input{cursor:pointer;line-height:1}.el-checkbox__input.is-disabled .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5;cursor:not-allowed}.el-checkbox__input.is-disabled .el-checkbox__inner::after{cursor:not-allowed;border-color:#b4bccc}.el-checkbox__input.is-disabled .el-checkbox__inner+.el-checkbox__label{cursor:not-allowed}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner::after{border-color:#b4bccc}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner{background-color:#edf2fc;border-color:#d8dce5}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner::before{background-color:#b4bccc;border-color:#b4bccc}.el-checkbox__input.is-checked .el-checkbox__inner,.el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#3C8DBC;border-color:#3C8DBC}.el-checkbox__input.is-disabled+span.el-checkbox__label{color:#b4bccc;cursor:not-allowed}.el-checkbox__input.is-checked .el-checkbox__inner::after{-webkit-transform:rotate(45deg) scaleY(1);transform:rotate(45deg) scaleY(1)}.el-checkbox__input.is-checked+.el-checkbox__label{color:#3C8DBC}.el-checkbox__input.is-focus .el-checkbox__inner{border-color:#3C8DBC}.el-checkbox__input.is-indeterminate .el-checkbox__inner::before{content:'';position:absolute;display:block;background-color:#fff;height:2px;-webkit-transform:scale(.5);transform:scale(.5);left:0;right:0;top:5px}.el-checkbox__input.is-indeterminate .el-checkbox__inner::after{display:none}.el-checkbox__inner{display:inline-block;position:relative;border:1px solid #d8dce5;border-radius:2px;-webkit-box-sizing:border-box;box-sizing:border-box;width:14px;height:14px;background-color:#fff;z-index:1;-webkit-transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46);transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46)}.el-checkbox__inner:hover{border-color:#3C8DBC}.el-checkbox__inner::after{-webkit-box-sizing:content-box;box-sizing:content-box;content:"";border:1px solid #fff;border-left:0;border-top:0;height:7px;left:4px;position:absolute;top:1px;-webkit-transform:rotate(45deg) scaleY(0);transform:rotate(45deg) scaleY(0);width:3px;-webkit-transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;transition:transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms,-webkit-transform .15s cubic-bezier(.71,-.46,.88,.6) 50ms;-webkit-transform-origin:center;transform-origin:center}.el-checkbox__original{opacity:0;outline:0;position:absolute;margin:0;width:0;height:0;left:-999px}.el-checkbox-button,.el-checkbox-button__inner{display:inline-block;position:relative}.el-checkbox__label{display:inline-block;padding-left:10px;line-height:19px;font-size:14px}.el-checkbox+.el-checkbox{margin-left:30px}.el-checkbox-button__inner{line-height:1;font-weight:500;cursor:pointer;background:#fff;border:1px solid #d8dce5;border-left:0;color:#5a5e66;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;padding:12px 20px;font-size:14px;border-radius:0}.el-checkbox-button__inner.is-round{padding:12px 20px}.el-checkbox-button__inner:hover{color:#3C8DBC}.el-checkbox-button__inner [class*=el-icon-]{line-height:.9}.el-checkbox-button__inner [class*=el-icon-]+span{margin-left:5px}.el-checkbox-button__original{opacity:0;outline:0;position:absolute;margin:0;left:-999px}.el-checkbox-button.is-checked .el-checkbox-button__inner{color:#fff;background-color:#3C8DBC;border-color:#3C8DBC;-webkit-box-shadow:-1px 0 0 0 rgb(138, 187, 215);box-shadow:-1px 0 0 0 rgb(138, 187, 215)}.el-checkbox-button.is-disabled .el-checkbox-button__inner{color:#b4bccc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#e6ebf5;-webkit-box-shadow:none;box-shadow:none}.el-checkbox-button:first-child .el-checkbox-button__inner{border-left:1px solid #d8dce5;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.el-checkbox-button.is-focus .el-checkbox-button__inner{border-color:#3C8DBC}.el-checkbox-button:last-child .el-checkbox-button__inner{border-radius:0 4px 4px 0}.el-checkbox-button--medium .el-checkbox-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-checkbox-button--medium .el-checkbox-button__inner.is-round{padding:10px 20px}.el-checkbox-button--small .el-checkbox-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-checkbox-button--small .el-checkbox-button__inner.is-round{padding:9px 15px}.el-checkbox-button--mini .el-checkbox-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-checkbox-button--mini .el-checkbox-button__inner.is-round{padding:7px 15px}.el-checkbox-group{font-size:0} -------------------------------------------------------------------------------- /app/web/theme/base.css: -------------------------------------------------------------------------------- 1 | .el-fade-in-enter,.el-fade-in-leave-active,.el-fade-in-linear-enter,.el-fade-in-linear-leave,.el-fade-in-linear-leave-active,.fade-in-linear-enter,.fade-in-linear-leave,.fade-in-linear-leave-active{opacity:0}.fade-in-linear-enter-active,.fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.el-fade-in-enter-active,.el-fade-in-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter,.el-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:center top;transform-origin:center top}.el-zoom-in-top-enter,.el-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:center bottom;transform-origin:center bottom}.el-zoom-in-bottom-enter,.el-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1,1);transform:scale(1,1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s;transition:transform .3s cubic-bezier(.23,1,.32,1) .1s,opacity .3s cubic-bezier(.23,1,.32,1) .1s,-webkit-transform .3s cubic-bezier(.23,1,.32,1) .1s;-webkit-transform-origin:top left;transform-origin:top left}.el-zoom-in-left-enter,.el-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45,.45);transform:scale(.45,.45)}.collapse-transition{-webkit-transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out;transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out}.horizontal-collapse-transition{-webkit-transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out;transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out}.el-list-enter-active,.el-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.el-list-enter,.el-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.el-opacity-transition{-webkit-transition:opacity .3s cubic-bezier(.55,0,.1,1);transition:opacity .3s cubic-bezier(.55,0,.1,1)}@font-face{font-family:element-icons;src:url(fonts/element-icons.woff?t=1508751886602) format("woff"),url(fonts/element-icons.ttf?t=1508751886602) format("truetype");font-weight:400;font-style:normal}[class*=" el-icon-"],[class^=el-icon-]{font-family:element-icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-icon-upload:before{content:"\e60d"}.el-icon-error:before{content:"\e62c"}.el-icon-success:before{content:"\e62d"}.el-icon-warning:before{content:"\e62e"}.el-icon-sort-down:before{content:"\e630"}.el-icon-sort-up:before{content:"\e631"}.el-icon-arrow-left:before{content:"\e600"}.el-icon-circle-plus:before{content:"\e601"}.el-icon-circle-plus-outline:before{content:"\e602"}.el-icon-arrow-down:before{content:"\e603"}.el-icon-arrow-right:before{content:"\e604"}.el-icon-arrow-up:before{content:"\e605"}.el-icon-back:before{content:"\e606"}.el-icon-circle-close:before{content:"\e607"}.el-icon-date:before{content:"\e608"}.el-icon-circle-close-outline:before{content:"\e609"}.el-icon-caret-left:before{content:"\e60a"}.el-icon-caret-bottom:before{content:"\e60b"}.el-icon-caret-top:before{content:"\e60c"}.el-icon-caret-right:before{content:"\e60e"}.el-icon-close:before{content:"\e60f"}.el-icon-d-arrow-left:before{content:"\e610"}.el-icon-check:before{content:"\e611"}.el-icon-delete:before{content:"\e612"}.el-icon-d-arrow-right:before{content:"\e613"}.el-icon-document:before{content:"\e614"}.el-icon-d-caret:before{content:"\e615"}.el-icon-edit-outline:before{content:"\e616"}.el-icon-download:before{content:"\e617"}.el-icon-goods:before{content:"\e618"}.el-icon-search:before{content:"\e619"}.el-icon-info:before{content:"\e61a"}.el-icon-message:before{content:"\e61b"}.el-icon-edit:before{content:"\e61c"}.el-icon-location:before{content:"\e61d"}.el-icon-loading:before{content:"\e61e"}.el-icon-location-outline:before{content:"\e61f"}.el-icon-menu:before{content:"\e620"}.el-icon-minus:before{content:"\e621"}.el-icon-bell:before{content:"\e622"}.el-icon-mobile-phone:before{content:"\e624"}.el-icon-news:before{content:"\e625"}.el-icon-more:before{content:"\e646"}.el-icon-more-outline:before{content:"\e626"}.el-icon-phone:before{content:"\e627"}.el-icon-phone-outline:before{content:"\e628"}.el-icon-picture:before{content:"\e629"}.el-icon-picture-outline:before{content:"\e62a"}.el-icon-plus:before{content:"\e62b"}.el-icon-printer:before{content:"\e62f"}.el-icon-rank:before{content:"\e632"}.el-icon-refresh:before{content:"\e633"}.el-icon-question:before{content:"\e634"}.el-icon-remove:before{content:"\e635"}.el-icon-share:before{content:"\e636"}.el-icon-star-on:before{content:"\e637"}.el-icon-setting:before{content:"\e638"}.el-icon-circle-check:before{content:"\e639"}.el-icon-service:before{content:"\e63a"}.el-icon-sold-out:before{content:"\e63b"}.el-icon-remove-outline:before{content:"\e63c"}.el-icon-star-off:before{content:"\e63d"}.el-icon-circle-check-outline:before{content:"\e63e"}.el-icon-tickets:before{content:"\e63f"}.el-icon-sort:before{content:"\e640"}.el-icon-zoom-in:before{content:"\e641"}.el-icon-time:before{content:"\e642"}.el-icon-view:before{content:"\e643"}.el-icon-upload2:before{content:"\e644"}.el-icon-zoom-out:before{content:"\e645"}.el-icon-loading{-webkit-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}.el-icon--right{margin-left:5px}.el-icon--left{margin-right:5px}@-webkit-keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}}@keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}} -------------------------------------------------------------------------------- /app/web/theme/input-number.css: -------------------------------------------------------------------------------- 1 | .el-input{position:relative;font-size:14px;display:inline-block;width:100%}.el-input::-webkit-scrollbar{z-index:11;width:6px}.el-input::-webkit-scrollbar:horizontal{height:6px}.el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.el-input::-webkit-scrollbar-corner{background:#fff}.el-input::-webkit-scrollbar-track{background:#fff}.el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #d8dce5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#5a5e66;display:inline-block;font-size:inherit;height:40px;line-height:1;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.el-input__prefix,.el-input__suffix{position:absolute;top:0;-webkit-transition:all .3s;height:100%;color:#b4bccc;text-align:center}.el-input__inner::-webkit-input-placeholder{color:#b4bccc}.el-input__inner:-ms-input-placeholder{color:#b4bccc}.el-input__inner::placeholder{color:#b4bccc}.el-input__inner:hover{border-color:#b4bccc}.el-input.is-active .el-input__inner,.el-input__inner:focus{border-color:#3C8DBC;outline:0}.el-input__suffix{right:5px;transition:all .3s;pointer-events:none}.el-input__suffix-inner{pointer-events:all}.el-input__prefix{left:5px;transition:all .3s}.el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.el-input__validateIcon{pointer-events:none}.el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#b4bccc}.el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#b4bccc}.el-input.is-disabled .el-input__inner::placeholder{color:#b4bccc}.el-input.is-disabled .el-input__icon{cursor:not-allowed}.el-input--suffix .el-input__inner{padding-right:30px}.el-input--prefix .el-input__inner{padding-left:30px}.el-input--medium{font-size:14px}.el-input--medium .el-input__inner{height:36px}.el-input--medium .el-input__icon{line-height:36px}.el-input--small{font-size:13px}.el-input--small .el-input__inner{height:32px}.el-input--small .el-input__icon{line-height:32px}.el-input--mini{font-size:12px}.el-input--mini .el-input__inner{height:28px}.el-input--mini .el-input__icon{line-height:28px}.el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate}.el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.el-input-group__append,.el-input-group__prepend{background-color:#f5f7fa;color:#878d99;vertical-align:middle;display:table-cell;position:relative;border:1px solid #d8dce5;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.el-input-group--prepend .el-input__inner,.el-input-group__append{border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--append .el-input__inner,.el-input-group__prepend{border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group__append:focus,.el-input-group__prepend:focus{outline:0}.el-input-group__append .el-button,.el-input-group__append .el-select,.el-input-group__prepend .el-button,.el-input-group__prepend .el-select{display:inline-block;margin:-20px}.el-input-group__append button.el-button,.el-input-group__append div.el-select .el-input__inner,.el-input-group__append div.el-select:hover .el-input__inner,.el-input-group__prepend button.el-button,.el-input-group__prepend div.el-select .el-input__inner,.el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.el-input-group__append .el-button,.el-input-group__append .el-input,.el-input-group__prepend .el-button,.el-input-group__prepend .el-input{font-size:inherit}.el-input-group__prepend{border-right:0}.el-input-group__append{border-left:0}.el-textarea{display:inline-block;width:100%;vertical-align:bottom}.el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:14px;color:#5a5e66;background-color:#fff;background-image:none;border:1px solid #d8dce5;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.el-textarea__inner::placeholder{color:#b4bccc}.el-textarea__inner:hover{border-color:#b4bccc}.el-textarea__inner:focus{outline:0;border-color:#3C8DBC}.el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#dfe4ed;color:#b4bccc;cursor:not-allowed}.el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#b4bccc}.el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#b4bccc}.el-textarea.is-disabled .el-textarea__inner::placeholder{color:#b4bccc}.el-input-number{position:relative;display:inline-block;width:180px;line-height:38px}.el-input-number .el-input{display:block}.el-input-number .el-input__inner{-webkit-appearance:none;padding-left:50px;padding-right:50px;text-align:center}.el-input-number__decrease,.el-input-number__increase{position:absolute;z-index:1;top:1px;width:40px;height:auto;text-align:center;background:#f5f7fa;color:#5a5e66;cursor:pointer;font-size:13px}.el-input-number__decrease:hover,.el-input-number__increase:hover{color:#3C8DBC}.el-input-number__decrease:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled),.el-input-number__increase:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled){border-color:#3C8DBC}.el-input-number__decrease.is-disabled,.el-input-number__increase.is-disabled{color:#b4bccc;cursor:not-allowed}.el-input-number__increase{right:1px;border-radius:0 4px 4px 0;border-left:1px solid #d8dce5}.el-input-number__decrease{left:1px;border-radius:4px 0 0 4px;border-right:1px solid #d8dce5}.el-input-number.is-disabled .el-input-number__decrease,.el-input-number.is-disabled .el-input-number__increase{border-color:#dfe4ed;color:#dfe4ed}.el-input-number.is-disabled .el-input-number__decrease:hover,.el-input-number.is-disabled .el-input-number__increase:hover{color:#dfe4ed;cursor:not-allowed}.el-input-number--medium{width:200px;line-height:34px}.el-input-number--medium .el-input-number__decrease,.el-input-number--medium .el-input-number__increase{width:36px;font-size:14px}.el-input-number--medium .el-input__inner{padding-left:43px;padding-right:43px}.el-input-number--small{width:130px;line-height:30px}.el-input-number--small .el-input-number__decrease,.el-input-number--small .el-input-number__increase{width:32px;font-size:13px}.el-input-number--small .el-input-number__decrease [class*=el-icon],.el-input-number--small .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.9);transform:scale(.9)}.el-input-number--small .el-input__inner{padding-left:39px;padding-right:39px}.el-input-number--mini{width:130px;line-height:26px}.el-input-number--mini .el-input-number__decrease,.el-input-number--mini .el-input-number__increase{width:28px;font-size:12px}.el-input-number--mini .el-input-number__decrease [class*=el-icon],.el-input-number--mini .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.el-input-number--mini .el-input__inner{padding-left:35px;padding-right:35px}.el-input-number.is-without-controls .el-input__inner{padding-left:15px;padding-right:15px}.el-input-number.is-controls-right .el-input__inner{padding-left:15px;padding-right:50px}.el-input-number.is-controls-right .el-input-number__decrease,.el-input-number.is-controls-right .el-input-number__increase{height:auto;line-height:19px}.el-input-number.is-controls-right .el-input-number__decrease [class*=el-icon],.el-input-number.is-controls-right .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.el-input-number.is-controls-right .el-input-number__increase{border-radius:0 4px 0 0;border-bottom:1px solid #d8dce5}.el-input-number.is-controls-right .el-input-number__decrease{right:1px;bottom:1px;top:auto;left:auto;border-right:none;border-left:1px solid #d8dce5;border-radius:0 0 4px}.el-input-number.is-controls-right[class*=medium] [class*=decrease],.el-input-number.is-controls-right[class*=medium] [class*=increase]{line-height:17px}.el-input-number.is-controls-right[class*=small] [class*=decrease],.el-input-number.is-controls-right[class*=small] [class*=increase]{line-height:15px}.el-input-number.is-controls-right[class*=mini] [class*=decrease],.el-input-number.is-controls-right[class*=mini] [class*=increase]{line-height:13px} --------------------------------------------------------------------------------