├── vue-admin ├── static │ ├── .gitkeep │ └── img │ │ ├── 100.0447fe9.png │ │ ├── 101.1415c1c.png │ │ └── 102.58ce423.png ├── build │ ├── logo.png │ ├── vue-loader.conf.js │ ├── build.js │ ├── check-versions.js │ ├── webpack.base.conf.js │ ├── utils.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── config │ ├── prod.env.js │ ├── dev.env.js │ └── index.js ├── src │ ├── assets │ │ ├── 100.png │ │ ├── 101.png │ │ ├── 102.png │ │ └── logo.png │ ├── views │ │ ├── layout │ │ │ ├── components │ │ │ │ ├── index.js │ │ │ │ ├── AppView.vue │ │ │ │ ├── Sidebar │ │ │ │ │ ├── Sidebaritem.vue │ │ │ │ │ └── index.vue │ │ │ │ ├── TagsView.vue │ │ │ │ └── Navbar.vue │ │ │ ├── TagsView.vue │ │ │ └── Layout.vue │ │ ├── table │ │ │ ├── tabletree.vue │ │ │ ├── tabletree │ │ │ │ ├── tabletree1.vue │ │ │ │ └── tabletree2.vue │ │ │ ├── tablerank.vue │ │ │ ├── tabletwo.vue │ │ │ └── tableone.vue │ │ ├── map │ │ │ └── mapExample.vue │ │ ├── calendar │ │ │ └── calendarExample.vue │ │ ├── login │ │ │ └── login.vue │ │ └── home │ │ │ └── home.vue │ ├── styles │ │ ├── variables.scss │ │ ├── transition.scss │ │ ├── element-ui.scss │ │ ├── mixin.scss │ │ ├── btn.scss │ │ ├── sidebar.scss │ │ └── index.scss │ ├── utils │ │ ├── createUniqueString.js │ │ ├── i18n.js │ │ ├── auth.js │ │ ├── clipboard.js │ │ ├── openWindow.js │ │ ├── validate.js │ │ ├── request.js │ │ └── index.js │ ├── store │ │ ├── index.js │ │ └── modules │ │ │ ├── user.js │ │ │ └── app.js │ ├── main.js │ ├── api │ │ └── login.js │ ├── App.vue │ ├── router │ │ ├── index.js │ │ └── nav.config.json │ └── components │ │ └── loginpage.vue ├── .editorconfig ├── .gitignore ├── .babelrc ├── .postcssrc.js ├── index.html ├── README.md └── package.json ├── layout.png ├── login.gif ├── express ├── views │ ├── static │ │ ├── img │ │ │ ├── 100.0447fe9.png │ │ │ ├── 101.1415c1c.png │ │ │ └── 102.58ce423.png │ │ ├── fonts │ │ │ └── element-icons.6f0a763.ttf │ │ └── js │ │ │ ├── manifest.19edb7b930d13abfaf18.js │ │ │ └── manifest.19edb7b930d13abfaf18.js.map │ └── index.html ├── public │ ├── static │ │ ├── img │ │ │ ├── 100.0447fe9.png │ │ │ ├── 101.1415c1c.png │ │ │ └── 102.58ce423.png │ │ ├── fonts │ │ │ └── element-icons.6f0a763.ttf │ │ └── js │ │ │ ├── manifest.205e82be084d477ab0df.js │ │ │ └── manifest.205e82be084d477ab0df.js.map │ └── index.html ├── routes │ ├── index.js │ ├── table.js │ └── users.js ├── package.json ├── config │ ├── db.js │ └── token.js ├── package-lock.json ├── bin │ └── www └── app.js ├── .gitignore └── README.md /vue-admin/static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/layout.png -------------------------------------------------------------------------------- /login.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/login.gif -------------------------------------------------------------------------------- /vue-admin/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/vue-admin/build/logo.png -------------------------------------------------------------------------------- /vue-admin/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /vue-admin/src/assets/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/vue-admin/src/assets/100.png -------------------------------------------------------------------------------- /vue-admin/src/assets/101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/vue-admin/src/assets/101.png -------------------------------------------------------------------------------- /vue-admin/src/assets/102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/vue-admin/src/assets/102.png -------------------------------------------------------------------------------- /vue-admin/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/vue-admin/src/assets/logo.png -------------------------------------------------------------------------------- /vue-admin/static/img/100.0447fe9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/vue-admin/static/img/100.0447fe9.png -------------------------------------------------------------------------------- /vue-admin/static/img/101.1415c1c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/vue-admin/static/img/101.1415c1c.png -------------------------------------------------------------------------------- /vue-admin/static/img/102.58ce423.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/vue-admin/static/img/102.58ce423.png -------------------------------------------------------------------------------- /express/views/static/img/100.0447fe9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/express/views/static/img/100.0447fe9.png -------------------------------------------------------------------------------- /express/views/static/img/101.1415c1c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/express/views/static/img/101.1415c1c.png -------------------------------------------------------------------------------- /express/views/static/img/102.58ce423.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/express/views/static/img/102.58ce423.png -------------------------------------------------------------------------------- /express/public/static/img/100.0447fe9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/express/public/static/img/100.0447fe9.png -------------------------------------------------------------------------------- /express/public/static/img/101.1415c1c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/express/public/static/img/101.1415c1c.png -------------------------------------------------------------------------------- /express/public/static/img/102.58ce423.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/express/public/static/img/102.58ce423.png -------------------------------------------------------------------------------- /express/public/static/fonts/element-icons.6f0a763.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/express/public/static/fonts/element-icons.6f0a763.ttf -------------------------------------------------------------------------------- /express/views/static/fonts/element-icons.6f0a763.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuyanming/vue-express/HEAD/express/views/static/fonts/element-icons.6f0a763.ttf -------------------------------------------------------------------------------- /vue-admin/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /vue-admin/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /vue-admin/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /vue-admin/src/views/layout/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Navbar } from './Navbar' 2 | export { default as SideBar } from './Sidebar/index.vue' 3 | export { default as TagsView } from './TagsView' 4 | export { default as AppView } from './AppView' 5 | -------------------------------------------------------------------------------- /express/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | // router.get('/', function(req, res, next) { 6 | // res.render('index', { title: 'Express' }); 7 | // }); 8 | // 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /vue-admin/src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | $blue:#324157; 2 | $light-blue:#3A71A8; 3 | $red:#C03639; 4 | $pink: #E65D6E; 5 | $green: #30B08F; 6 | $tiffany: #4AB7BD; 7 | $yellow:#FEC171; 8 | $panGreen: #30B08F; 9 | 10 | //sidebar 11 | $menuBg:#304156; 12 | $subMenuBg:#1f2d3d; 13 | $menuHover:#001528; 14 | -------------------------------------------------------------------------------- /vue-admin/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"] 12 | } 13 | -------------------------------------------------------------------------------- /vue-admin/src/utils/createUniqueString.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiachenpan on 17/3/8. 3 | */ 4 | export default function createUniqueString() { 5 | const timestamp = +new Date() + '' 6 | const randomNum = parseInt((1 + Math.random()) * 65536) + '' 7 | return (+(randomNum + timestamp)).toString(32) 8 | } 9 | -------------------------------------------------------------------------------- /vue-admin/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /vue-admin/src/utils/i18n.js: -------------------------------------------------------------------------------- 1 | // translate router.meta.title, be used in breadcrumb sidebar tagsview 2 | export function generateTitle(title) { 3 | const hasKey = this.$te('route.' + title) 4 | const translatedTitle = this.$t('route.' + title) // $t :this method from vue-i18n, inject in @/lang/index.js 5 | 6 | if (hasKey) { 7 | return translatedTitle 8 | } 9 | return title 10 | } 11 | -------------------------------------------------------------------------------- /vue-admin/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | 4 | import app from './modules/app'; 5 | import user from './modules/user'; 6 | 7 | Vue.use(Vuex); 8 | 9 | const store = new Vuex.Store({ 10 | state: { 11 | // 12 | }, 13 | mutations: { 14 | // 15 | }, 16 | actions: { 17 | 18 | }, 19 | modules: { 20 | app, 21 | user 22 | } 23 | }); 24 | 25 | export default store; 26 | -------------------------------------------------------------------------------- /vue-admin/src/views/layout/components/AppView.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 21 | -------------------------------------------------------------------------------- /express/public/index.html: -------------------------------------------------------------------------------- 1 | my-express
-------------------------------------------------------------------------------- /express/views/index.html: -------------------------------------------------------------------------------- 1 | my-express
-------------------------------------------------------------------------------- /vue-admin/src/utils/auth.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const TokenKey = 'access_token' 4 | 5 | export function getToken() { 6 | return Cookies.get(TokenKey) 7 | } 8 | 9 | export function setToken(token) { 10 | return Cookies.set(TokenKey, token) 11 | } 12 | export function removeToken() { 13 | return Cookies.remove(TokenKey) 14 | } 15 | export function setusername(data) { 16 | return Cookies.set('username', data) 17 | } 18 | export function getusername() { 19 | return Cookies.get('username') 20 | } 21 | 22 | -------------------------------------------------------------------------------- /vue-admin/src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import router from './router' 6 | import ElementUI from 'element-ui'; 7 | import 'element-ui/lib/theme-chalk/index.css'; 8 | import store from './store' 9 | Vue.config.productionTip = false 10 | Vue.use(ElementUI); 11 | new Vue({ 12 | el: '#app', 13 | router, 14 | store, 15 | components: { App }, 16 | template: '' 17 | }) 18 | -------------------------------------------------------------------------------- /vue-admin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | my-express 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /vue-admin/README.md: -------------------------------------------------------------------------------- 1 | # my-express 2 | 3 | > A Vue.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | 17 | # build for production and view the bundle analyzer report 18 | npm run build --report 19 | ``` 20 | 21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 22 | -------------------------------------------------------------------------------- /express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "myapp", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www", 7 | "build": "node build/build.js", 8 | "dev": "node-dev ./bin/www" 9 | }, 10 | "dependencies": { 11 | "consolidate": "^0.15.1", 12 | "cookie-parser": "~1.4.3", 13 | "debug": "~2.6.9", 14 | "ejs": "^2.6.1", 15 | "express": "~4.16.0", 16 | "http-errors": "~1.6.2", 17 | "jade": "~1.11.0", 18 | "jsonwebtoken": "^8.3.0", 19 | "morgan": "~1.9.0", 20 | "mysql": "^2.15.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vue-admin/src/styles/transition.scss: -------------------------------------------------------------------------------- 1 | //globl transition css 2 | 3 | /*fade*/ 4 | .fade-enter-active, 5 | .fade-leave-active { 6 | transition: opacity 0.28s; 7 | } 8 | 9 | .fade-enter, 10 | .fade-leave-active { 11 | opacity: 0; 12 | } 13 | 14 | /*fade*/ 15 | .breadcrumb-enter-active, 16 | .breadcrumb-leave-active { 17 | transition: all .5s; 18 | } 19 | 20 | .breadcrumb-enter, 21 | .breadcrumb-leave-active { 22 | opacity: 0; 23 | transform: translateX(20px); 24 | } 25 | 26 | .breadcrumb-move { 27 | transition: all .5s; 28 | } 29 | 30 | .breadcrumb-leave-active { 31 | position: absolute; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /express/config/db.js: -------------------------------------------------------------------------------- 1 | var mysql = require("mysql"); 2 | var connection = mysql.createConnection({ 3 | host:"59.110.164.162", 4 | port: 3306, 5 | user:"root", 6 | password:"449a18c7ab3950ae", 7 | database:"test", 8 | useConnectionPooling: true 9 | }); 10 | 11 | function query(sql,data,callback){ 12 | // connection.connect() 13 | // pool.getConnection(function(err,connection){ 14 | connection.query(sql,data,function (err,rows) { 15 | callback(err,rows); 16 | // connection.release(); 17 | // connection.end() 18 | }); 19 | // }); 20 | } 21 | 22 | exports.query = query; -------------------------------------------------------------------------------- /vue-admin/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vue-admin/src/views/table/tabletree.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | -------------------------------------------------------------------------------- /vue-admin/src/store/modules/user.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie'; 2 | 3 | const user = { 4 | state: {}, 5 | mutations: { 6 | logout (state, vm) { 7 | Cookies.remove('user'); 8 | Cookies.remove('password'); 9 | Cookies.remove('access'); 10 | // 恢复默认样式 11 | let themeLink = document.querySelector('link[name="theme"]'); 12 | themeLink.setAttribute('href', ''); 13 | // 清空打开的页面等数据,但是保存主题数据 14 | let theme = ''; 15 | if (localStorage.theme) { 16 | theme = localStorage.theme; 17 | } 18 | localStorage.clear(); 19 | if (theme) { 20 | localStorage.theme = theme; 21 | } 22 | } 23 | } 24 | }; 25 | 26 | export default user; -------------------------------------------------------------------------------- /vue-admin/src/views/layout/components/Sidebar/Sidebaritem.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 38 | 39 | -------------------------------------------------------------------------------- /vue-admin/src/utils/clipboard.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Clipboard from 'clipboard' 3 | 4 | function clipboardSuccess() { 5 | Vue.prototype.$message({ 6 | message: 'Copy successfully', 7 | type: 'success', 8 | duration: 1500 9 | }) 10 | } 11 | 12 | function clipboardError() { 13 | Vue.prototype.$message({ 14 | message: 'Copy failed', 15 | type: 'error' 16 | }) 17 | } 18 | 19 | export default function handleClipboard(text, event) { 20 | const clipboard = new Clipboard(event.target, { 21 | text: () => text 22 | }) 23 | clipboard.on('success', () => { 24 | clipboardSuccess() 25 | clipboard.off('error') 26 | clipboard.off('success') 27 | clipboard.destroy() 28 | }) 29 | clipboard.on('error', () => { 30 | clipboardError() 31 | clipboard.off('error') 32 | clipboard.off('success') 33 | clipboard.destroy() 34 | }) 35 | clipboard.onClick(event) 36 | } 37 | -------------------------------------------------------------------------------- /express/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "myapp", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "bluebird": { 8 | "version": "3.5.1", 9 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 10 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" 11 | }, 12 | "consolidate": { 13 | "version": "0.15.1", 14 | "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", 15 | "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", 16 | "requires": { 17 | "bluebird": "3.5.1" 18 | } 19 | }, 20 | "ejs": { 21 | "version": "2.6.1", 22 | "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", 23 | "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vue-admin/src/views/table/tabletree/tabletree1.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /vue-admin/src/views/table/tabletree/tabletree2.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /vue-admin/src/utils/openWindow.js: -------------------------------------------------------------------------------- 1 | /** 2 | *Created by jiachenpan on 16/11/29. 3 | * @param {Sting} url 4 | * @param {Sting} title 5 | * @param {Number} w 6 | * @param {Number} h 7 | */ 8 | 9 | export default function openWindow(url, title, w, h) { 10 | // Fixes dual-screen position Most browsers Firefox 11 | const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left 12 | const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top 13 | 14 | const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width 15 | const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height 16 | 17 | const left = ((width / 2) - (w / 2)) + dualScreenLeft 18 | const top = ((height / 2) - (h / 2)) + dualScreenTop 19 | const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left) 20 | 21 | // Puts focus on the newWindow 22 | if (window.focus) { 23 | newWindow.focus() 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /vue-admin/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /vue-admin/src/utils/validate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiachenpan on 16/11/18. 3 | */ 4 | 5 | export function isvalidUsername(str) { 6 | const valid_map = ['admin', 'editor'] 7 | return valid_map.indexOf(str.trim()) >= 0 8 | } 9 | 10 | /* 合法uri*/ 11 | export function validateURL(textval) { 12 | const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ 13 | return urlregex.test(textval) 14 | } 15 | 16 | /* 小写字母*/ 17 | export function validateLowerCase(str) { 18 | const reg = /^[a-z]+$/ 19 | return reg.test(str) 20 | } 21 | 22 | /* 大写字母*/ 23 | export function validateUpperCase(str) { 24 | const reg = /^[A-Z]+$/ 25 | return reg.test(str) 26 | } 27 | 28 | /* 大小写字母*/ 29 | export function validatAlphabets(str) { 30 | const reg = /^[A-Za-z]+$/ 31 | return reg.test(str) 32 | } 33 | 34 | /** 35 | * validate email 36 | * @param email 37 | * @returns {boolean} 38 | */ 39 | export function validateEmail(email) { 40 | const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 41 | return re.test(email) 42 | } 43 | 44 | -------------------------------------------------------------------------------- /vue-admin/src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import router from '../router' 3 | import { Message } from 'element-ui' 4 | import { getToken } from './auth' 5 | const qs = require('querystring'); 6 | // 创建axios实例 7 | const service = axios.create({ 8 | baseURL: 'http://localhost:3000/', // api的base_url 9 | timeout: 20000 // 请求超时时间 10 | 11 | }) 12 | 13 | service.interceptors.request.use(config => { 14 | console.log(config) 15 | if(config.method == 'post'){ 16 | config.data = qs.stringify(config.data) 17 | } 18 | config.headers['access_token'] =getToken() 19 | return config 20 | }, error => { 21 | return Promise.reject(error) 22 | }) 23 | 24 | // respone拦截器 25 | service.interceptors.response.use( 26 | response => { 27 | if(response.data.meta.code == '401'){ 28 | Message({ 29 | message: "请重新登录", 30 | type: 'error', 31 | duration: 1500 32 | }) 33 | router.push('/login') 34 | }else if(response.data.meta.code == '500'){ 35 | Message({ 36 | message: response.data.meta.message, 37 | type: 'error', 38 | duration: 1500 39 | }) 40 | } 41 | return response 42 | }, 43 | error => { 44 | console.log(error) 45 | Message({ 46 | message: error, 47 | type: 'error', 48 | duration: 1500 49 | }) 50 | } 51 | ) 52 | 53 | export default service 54 | -------------------------------------------------------------------------------- /vue-admin/src/styles/element-ui.scss: -------------------------------------------------------------------------------- 1 | //覆盖一些element-ui样式 2 | 3 | .el-breadcrumb__inner, .el-breadcrumb__inner a{ 4 | font-weight: 400!important; 5 | } 6 | 7 | .el-upload { 8 | input[type="file"] { 9 | display: none !important; 10 | } 11 | } 12 | 13 | .el-upload__input { 14 | display: none; 15 | } 16 | 17 | .cell { 18 | .el-tag { 19 | margin-right: 0px; 20 | } 21 | } 22 | 23 | .small-padding { 24 | .cell { 25 | padding-left: 5px; 26 | padding-right: 5px; 27 | } 28 | } 29 | 30 | .fixed-width{ 31 | .el-button--mini{ 32 | padding: 7px 10px; 33 | width: 60px; 34 | } 35 | } 36 | 37 | .status-col { 38 | .cell { 39 | padding: 0 10px; 40 | text-align: center; 41 | .el-tag { 42 | margin-right: 0px; 43 | } 44 | } 45 | } 46 | 47 | //暂时性解决dialog 问题 https://github.com/ElemeFE/element/issues/2461 48 | .el-dialog { 49 | transform: none; 50 | left: 0; 51 | position: relative; 52 | margin: 0 auto; 53 | } 54 | 55 | //文章页textarea修改样式 56 | .article-textarea { 57 | textarea { 58 | padding-right: 40px; 59 | resize: none; 60 | border: none; 61 | border-radius: 0px; 62 | border-bottom: 1px solid #bfcbd9; 63 | } 64 | } 65 | 66 | //element ui upload 67 | .upload-container { 68 | .el-upload { 69 | width: 100%; 70 | .el-upload-dragger { 71 | width: 100%; 72 | height: 200px; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /express/public/static/js/manifest.205e82be084d477ab0df.js: -------------------------------------------------------------------------------- 1 | !function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,c,i){for(var u,a,f,s=0,l=[];s 2 | 5 | 9 | 10 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /vue-admin/src/styles/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix { 2 | &:after { 3 | content: ""; 4 | display: table; 5 | clear: both; 6 | } 7 | } 8 | 9 | @mixin scrollBar { 10 | &::-webkit-scrollbar-track-piece { 11 | background: #d3dce6; 12 | } 13 | &::-webkit-scrollbar { 14 | width: 6px; 15 | } 16 | &::-webkit-scrollbar-thumb { 17 | background: #99a9bf; 18 | border-radius: 20px; 19 | } 20 | } 21 | 22 | @mixin relative { 23 | position: relative; 24 | width: 100%; 25 | height: 100%; 26 | } 27 | 28 | @mixin pct($pct) { 29 | width: #{$pct}; 30 | position: relative; 31 | margin: 0 auto; 32 | } 33 | 34 | @mixin triangle($width, $height, $color, $direction) { 35 | $width: $width/2; 36 | $color-border-style: $height solid $color; 37 | $transparent-border-style: $width solid transparent; 38 | height: 0; 39 | width: 0; 40 | @if $direction==up { 41 | border-bottom: $color-border-style; 42 | border-left: $transparent-border-style; 43 | border-right: $transparent-border-style; 44 | } 45 | @else if $direction==right { 46 | border-left: $color-border-style; 47 | border-top: $transparent-border-style; 48 | border-bottom: $transparent-border-style; 49 | } 50 | @else if $direction==down { 51 | border-top: $color-border-style; 52 | border-left: $transparent-border-style; 53 | border-right: $transparent-border-style; 54 | } 55 | @else if $direction==left { 56 | border-right: $color-border-style; 57 | border-top: $transparent-border-style; 58 | border-bottom: $transparent-border-style; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /vue-admin/src/styles/btn.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | 3 | @mixin colorBtn($color) { 4 | background: $color; 5 | &:hover { 6 | color: $color; 7 | &:before, 8 | &:after { 9 | background: $color; 10 | } 11 | } 12 | } 13 | 14 | .blue-btn { 15 | @include colorBtn($blue) 16 | } 17 | 18 | .light-blue-btn { 19 | @include colorBtn($light-blue) 20 | } 21 | 22 | .red-btn { 23 | @include colorBtn($red) 24 | } 25 | 26 | .pink-btn { 27 | @include colorBtn($pink) 28 | } 29 | 30 | .green-btn { 31 | @include colorBtn($green) 32 | } 33 | 34 | .tiffany-btn { 35 | @include colorBtn($tiffany) 36 | } 37 | 38 | .yellow-btn { 39 | @include colorBtn($yellow) 40 | } 41 | 42 | .pan-btn { 43 | font-size: 14px; 44 | color: #fff; 45 | padding: 14px 36px; 46 | border-radius: 8px; 47 | border: none; 48 | outline: none; 49 | margin-right: 25px; 50 | transition: 600ms ease all; 51 | position: relative; 52 | display: inline-block; 53 | &:hover { 54 | background: #fff; 55 | &:before, 56 | &:after { 57 | width: 100%; 58 | transition: 600ms ease all; 59 | } 60 | } 61 | &:before, 62 | &:after { 63 | content: ''; 64 | position: absolute; 65 | top: 0; 66 | right: 0; 67 | height: 2px; 68 | width: 0; 69 | transition: 400ms ease all; 70 | } 71 | &::after { 72 | right: inherit; 73 | top: inherit; 74 | left: 0; 75 | bottom: 0; 76 | } 77 | } 78 | 79 | .custom-button { 80 | display: inline-block; 81 | line-height: 1; 82 | white-space: nowrap; 83 | cursor: pointer; 84 | background: #fff; 85 | color: #fff; 86 | -webkit-appearance: none; 87 | text-align: center; 88 | box-sizing: border-box; 89 | outline: 0; 90 | margin: 0; 91 | padding: 10px 15px; 92 | font-size: 14px; 93 | border-radius: 4px; 94 | } 95 | 96 | -------------------------------------------------------------------------------- /express/routes/table.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var db = require("../config/db"); 4 | const jwt = require('jsonwebtoken') 5 | const token = require("../config/token") 6 | var data={data:'',meta:{code:'200',message:''}} 7 | /* GET users listing. */ 8 | // var meta={'code':'','message':'','date':''} 9 | router.post('/addinfo', function(req, res, next) { 10 | let table = req.body; 11 | db.query("INSERT INTO `table` (`date`,`name`,`province`,`city`,`address`,`zip`) VALUES (?,?,?,?,?,?)",[table.date,table.name,table.province,table.city,table.address,table.zip],function(err,rows){ 12 | data={data:'',meta:{code:'200',message:''}} 13 | res.send(data) 14 | }); 15 | 16 | }); 17 | router.get('/tablelist', function(req, res, next) { 18 | let query = req.query; 19 | let sql="SELECT * FROM `table`" 20 | if(query.user){ 21 | sql+= "where name="+query.user 22 | } 23 | if(query.region){ 24 | sql+= "where zip="+query.region 25 | } 26 | db.query(sql,[],function(err,rows){ 27 | data={data:rows,meta:{code:'200',message:''}} 28 | res.send(data) 29 | }) 30 | 31 | }); 32 | router.post('/delinfo', function(req, res, next) { 33 | let row = req.body.rows; 34 | 35 | let sql="delete from `table` where id ="+row 36 | db.query(sql,[],function(err,rows){ 37 | data={data:'',meta:{code:'200',message:''}} 38 | res.send(data) 39 | }) 40 | 41 | }); 42 | router.post('/upinfo', function(req, res, next) { 43 | let row = req.body; 44 | db.query("UPDATE `table` SET name='"+row.name+"',date='"+row.date+"',province='"+row.province+"',city='"+row.city+"',address='"+row.address+"',zip='"+row.zip+"' where `id`='"+row.id+"'",[],function(err,rows){ 45 | data={data:'',meta:{code:'200',message:''}} 46 | res.send(data) 47 | }); 48 | 49 | }); 50 | 51 | module.exports = router; 52 | -------------------------------------------------------------------------------- /vue-admin/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 96 | -------------------------------------------------------------------------------- /vue-admin/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Layout from '@/views/layout/Layout' 4 | import home from '@/views/home/home' 5 | import tabletree from '@/views/table/tabletree' 6 | const comp={Layout,tabletree} 7 | Vue.use(Router) 8 | import navConfig from './nav.config.json' 9 | const LOAD_DOCS_MAP = (parentpath,lang) => { 10 | console.log(`@/views${(parentpath ? parentpath+'/' : lang.path) + lang.path}.vue`) 11 | return r => require.ensure([], () => r(require(`@/views${(parentpath ? parentpath+'/' : lang.path) + lang.path}.vue`))); 12 | }; 13 | 14 | const loadDocs = function(parentpath,lang) { 15 | return LOAD_DOCS_MAP(parentpath,lang); 16 | }; 17 | const registerRoute = (navConfig,parentpath) => { 18 | let route = []; 19 | navConfig.forEach((lang, index) => { 20 | let navs = lang.children; 21 | if(parentpath){ 22 | lang.parentpath=parentpath+'/'+lang.path 23 | }else{ 24 | lang.parentpath=lang.path 25 | } 26 | if(lang.children){ 27 | route.push({ 28 | path: lang.parentpath, 29 | name:lang.name, 30 | redirect: `${lang.path}/${lang.children[0].path}`, 31 | component: comp[lang.component], 32 | meta:lang.meta, 33 | children: registerRoute(navs,lang.parentpath) 34 | }); 35 | }else{ 36 | route.push({ 37 | path: lang.parentpath, 38 | component: loadDocs(parentpath,lang), 39 | hidden: lang.hidden, 40 | name:lang.name, 41 | meta:lang.meta, 42 | }); 43 | } 44 | }); 45 | return route; 46 | }; 47 | 48 | let route = registerRoute(navConfig["router_zh"]); 49 | console.log(route) 50 | route.unshift({ 51 | path: "/", 52 | name:"home", 53 | redirect:"/home", 54 | component: Layout, 55 | meta: { title: "首页",icon: "el-icon-menu" }, 56 | children:[{ 57 | path: "/home", 58 | name: "homeindex", 59 | component: home, 60 | meta: { title: "首页",icon: "el-icon-menu" } 61 | },] 62 | }) 63 | 64 | export default new Router({ 65 | scrollBehavior: () => ({ y: 0 }), 66 | routes: route, 67 | }) 68 | -------------------------------------------------------------------------------- /vue-admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-express", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "xuyanming <15133652925@163.com>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js" 11 | }, 12 | "dependencies": { 13 | "axios": "^0.18.0", 14 | "element-ui": "^2.4.3", 15 | "vue": "^2.5.2", 16 | "vue-router": "^3.0.1" 17 | }, 18 | "devDependencies": { 19 | "autoprefixer": "^7.1.2", 20 | "babel-core": "^6.22.1", 21 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 22 | "babel-loader": "^7.1.1", 23 | "babel-plugin-syntax-jsx": "^6.18.0", 24 | "babel-plugin-transform-runtime": "^6.22.0", 25 | "babel-plugin-transform-vue-jsx": "^3.5.0", 26 | "babel-preset-env": "^1.3.2", 27 | "babel-preset-stage-2": "^6.22.0", 28 | "chalk": "^2.0.1", 29 | "copy-webpack-plugin": "^4.0.1", 30 | "css-loader": "^0.28.0", 31 | "extract-text-webpack-plugin": "^3.0.0", 32 | "file-loader": "^1.1.4", 33 | "friendly-errors-webpack-plugin": "^1.6.1", 34 | "html-webpack-plugin": "^2.30.1", 35 | "node-notifier": "^5.1.2", 36 | "node-sass": "^4.9.0", 37 | "optimize-css-assets-webpack-plugin": "^3.2.0", 38 | "ora": "^1.2.0", 39 | "portfinder": "^1.0.13", 40 | "postcss-import": "^11.0.0", 41 | "postcss-loader": "^2.0.8", 42 | "postcss-url": "^7.2.1", 43 | "rimraf": "^2.6.0", 44 | "sass-loader": "^7.0.3", 45 | "semver": "^5.3.0", 46 | "shelljs": "^0.7.6", 47 | "uglifyjs-webpack-plugin": "^1.1.1", 48 | "url-loader": "^0.5.8", 49 | "vue-loader": "^13.3.0", 50 | "vue-style-loader": "^3.0.1", 51 | "vue-template-compiler": "^2.5.2", 52 | "webpack": "^3.12.0", 53 | "webpack-bundle-analyzer": "^2.9.0", 54 | "webpack-dev-server": "^2.9.1", 55 | "webpack-merge": "^4.1.0" 56 | }, 57 | "engines": { 58 | "node": ">= 6.0.0", 59 | "npm": ">= 3.0.0" 60 | }, 61 | "browserslist": [ 62 | "> 1%", 63 | "last 2 versions", 64 | "not ie <= 8" 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /express/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('myapp:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | // var port = normalizePort('3000'); 15 | // app.set('port', port); 16 | 17 | const hostname = '0.0.0.0'; 18 | var port = normalizePort('3000'); 19 | app.set('port', port); 20 | var server = http.createServer(app); 21 | server.listen(port, hostname, () => { 22 | console.log(`Server running at http://${hostname}:${port}/`); 23 | }); 24 | 25 | /** 26 | * Create HTTP server. 27 | */ 28 | 29 | // var server = http.createServer(app); 30 | 31 | /** 32 | * Listen on provided port, on all network interfaces. 33 | */ 34 | 35 | // server.listen(port); 36 | server.on('error', onError); 37 | server.on('listening', onListening); 38 | 39 | /** 40 | * Normalize a port into a number, string, or false. 41 | */ 42 | 43 | function normalizePort(val) { 44 | var port = parseInt(val, 10); 45 | 46 | if (isNaN(port)) { 47 | // named pipe 48 | return val; 49 | } 50 | 51 | if (port >= 0) { 52 | // port number 53 | return port; 54 | } 55 | 56 | return false; 57 | } 58 | 59 | /** 60 | * Event listener for HTTP server "error" event. 61 | */ 62 | 63 | function onError(error) { 64 | if (error.syscall !== 'listen') { 65 | throw error; 66 | } 67 | 68 | var bind = typeof port === 'string' 69 | ? 'Pipe ' + port 70 | : 'Port ' + port; 71 | 72 | // handle specific listen errors with friendly messages 73 | switch (error.code) { 74 | case 'EACCES': 75 | console.error(bind + ' requires elevated privileges'); 76 | process.exit(1); 77 | break; 78 | case 'EADDRINUSE': 79 | console.error(bind + ' is already in use'); 80 | process.exit(1); 81 | break; 82 | default: 83 | throw error; 84 | } 85 | } 86 | 87 | /** 88 | * Event listener for HTTP server "listening" event. 89 | */ 90 | 91 | function onListening() { 92 | var addr = server.address(); 93 | var bind = typeof addr === 'string' 94 | ? 'pipe ' + addr 95 | : 'port ' + addr.port; 96 | debug('Listening on ' + bind); 97 | } 98 | -------------------------------------------------------------------------------- /vue-admin/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: {}, 14 | 15 | // Various Dev Server settings 16 | host: 'localhost', // can be overwritten by process.env.HOST 17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 18 | autoOpenBrowser: false, 19 | errorOverlay: true, 20 | notifyOnErrors: true, 21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 22 | 23 | 24 | /** 25 | * Source Maps 26 | */ 27 | 28 | // https://webpack.js.org/configuration/devtool/#development 29 | devtool: 'cheap-module-eval-source-map', 30 | 31 | // If you have problems debugging vue-files in devtools, 32 | // set this to false - it *may* help 33 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 34 | cacheBusting: true, 35 | 36 | cssSourceMap: true 37 | }, 38 | 39 | build: { 40 | // Template for index.html 41 | index: path.resolve(__dirname, '../dist/index.html'), 42 | 43 | // Paths 44 | assetsRoot: path.resolve(__dirname, '../dist'), 45 | assetsSubDirectory: 'static', 46 | assetsPublicPath: './', 47 | 48 | /** 49 | * Source Maps 50 | */ 51 | 52 | productionSourceMap: true, 53 | // https://webpack.js.org/configuration/devtool/#production 54 | devtool: '#source-map', 55 | 56 | // Gzip off by default as many popular static hosts such as 57 | // Surge or Netlify already gzip all static assets for you. 58 | // Before setting to `true`, make sure to: 59 | // npm install --save-dev compression-webpack-plugin 60 | productionGzip: false, 61 | productionGzipExtensions: ['js', 'css'], 62 | 63 | // Run the build command with an extra argument to 64 | // View the bundle analyzer report after build finishes: 65 | // `npm run build --report` 66 | // Set to `true` or `false` to always turn it on or off 67 | bundleAnalyzerReport: process.env.npm_config_report 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /vue-admin/src/views/map/mapExample.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 53 | 67 | -------------------------------------------------------------------------------- /vue-admin/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | 12 | 13 | module.exports = { 14 | context: path.resolve(__dirname, '../'), 15 | entry: { 16 | app: './src/main.js' 17 | }, 18 | output: { 19 | path: config.build.assetsRoot, 20 | filename: '[name].js', 21 | publicPath: process.env.NODE_ENV === 'production' 22 | ? config.build.assetsPublicPath 23 | : config.dev.assetsPublicPath 24 | }, 25 | resolve: { 26 | extensions: ['.js', '.vue', '.json'], 27 | alias: { 28 | 'vue$': 'vue/dist/vue.esm.js', 29 | '@': resolve('src'), 30 | } 31 | }, 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.vue$/, 36 | loader: 'vue-loader', 37 | options: vueLoaderConfig 38 | }, 39 | { 40 | test: /\.js$/, 41 | loader: 'babel-loader', 42 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] 43 | }, 44 | { 45 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 46 | loader: 'url-loader', 47 | options: { 48 | limit: 10000, 49 | name: utils.assetsPath('img/[name].[hash:7].[ext]'), 50 | } 51 | }, 52 | { 53 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 54 | loader: 'url-loader', 55 | options: { 56 | limit: 10000, 57 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 58 | } 59 | }, 60 | { 61 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 62 | loader: 'url-loader', 63 | options: { 64 | limit: 10000, 65 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 66 | } 67 | } 68 | ] 69 | }, 70 | node: { 71 | // prevent webpack from injecting useless setImmediate polyfill because Vue 72 | // source contains it (although only uses it if it's native). 73 | setImmediate: false, 74 | // prevent webpack from injecting mocks to Node native modules 75 | // that does not make sense for the client 76 | dgram: 'empty', 77 | fs: 'empty', 78 | net: 'empty', 79 | tls: 'empty', 80 | child_process: 'empty' 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /express/app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var cookieParser = require('cookie-parser'); 5 | const bodyParser = require('body-parser') 6 | var logger = require('morgan'); 7 | const token = require("./config/token") 8 | var indexRouter = require('./routes/index'); 9 | var usersRouter = require('./routes/users'); 10 | var tableRouter = require('./routes/table'); 11 | const rouetpass =["/users/login","/users/add",'/']; 12 | var data={data:'',meta:{code:'',message:''}} 13 | 14 | var app = express(); 15 | app.all('*', function(req, res, next) { 16 | res.header("Access-Control-Allow-Origin", "*"); 17 | res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS'); 18 | res.header("Access-Control-Allow-Headers", "Content-Type, access_token, X-Requested-With") 19 | if(rouetpass.indexOf(req.originalUrl) > -1 || req.originalUrl.split('/').indexOf('static') > -1){ 20 | next() 21 | }else{ 22 | if (req.method != "OPTIONS"){ 23 | var accesstoken = req.headers['access_token']; 24 | let datatoken = token.decodeToken(accesstoken) 25 | if(datatoken.flag){ 26 | next() 27 | }else{ 28 | data.meta.code=401; 29 | res.send(data) 30 | } 31 | }else{ 32 | next() 33 | } 34 | } 35 | }); 36 | // app.set('views', path.join(__dirname, 'views')); 37 | // app.set('view engine', 'jade'); 38 | 39 | app.use(logger('dev')); 40 | app.use(express.json()); 41 | app.use(express.urlencoded({ extended: false })); 42 | app.use(cookieParser()); 43 | app.use(express.static(path.join(__dirname, 'public'))); 44 | app.get('/', function (req, res) { 45 | res.sendFile(path.resolve(__dirname, 'public',"index.html")); 46 | }); 47 | 48 | 49 | // app.use('/', indexRouter); 50 | app.use('/users', usersRouter); 51 | app.use('/table', tableRouter); 52 | // catch 404 and forward to error handler 53 | app.use(function(req, res, next) { 54 | next(createError(404)); 55 | }); 56 | 57 | // error handler 58 | app.use(function(err, req, res, next) { 59 | // set locals, only providing error in development 60 | res.locals.message = err.message; 61 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 62 | 63 | // render the error page 64 | res.status(err.status || 500); 65 | res.render('error'); 66 | }); 67 | 68 | module.exports = app; 69 | -------------------------------------------------------------------------------- /vue-admin/src/router/nav.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "router_zh": [ 3 | { 4 | "name": "login", 5 | "path": "/login", 6 | "hidden": true, 7 | "meta": { "title": "登录","icon": "" } 8 | }, 9 | { 10 | "path": "/table", 11 | "name": "table", 12 | "meta": { 13 | "title": "表格", 14 | "icon": "el-icon-menu" 15 | }, 16 | "component":"Layout", 17 | "children": [ 18 | { 19 | "path": "tableone", 20 | "name": "tableone", 21 | "meta": { "title": "单元格合并","icon": "el-icon-menu" } 22 | }, 23 | { 24 | "path": "tabletwo", 25 | "name": "tabletwo", 26 | "meta": { "title": "表格树", "icon": "el-icon-menu"} 27 | }, 28 | { 29 | "path": "tablerank", 30 | "name": "tablerank", 31 | "meta": { "title": "拖拽", "icon": "el-icon-rank"} 32 | }, 33 | { 34 | "path": "tabletree", 35 | "name": "tabletree", 36 | "meta": { "title": "表格二", "icon": "el-icon-menu"}, 37 | "component":"tabletree", 38 | "children": [ 39 | { 40 | "path": "tabletree1", 41 | "name": "tabletree1", 42 | "meta": { "title": "表格二1","icon": "el-icon-menu" } 43 | }, 44 | { 45 | "path": "tabletree2", 46 | "name": "tabletree2", 47 | "meta": { "title": "表格二2","icon": "el-icon-menu" } 48 | } 49 | ] 50 | } 51 | ] 52 | }, 53 | { 54 | "path": "/calendar", 55 | "name": "calendar", 56 | "meta": { 57 | "title": "日历", 58 | "icon": "el-icon-date" 59 | }, 60 | "component":"Layout", 61 | "children": [ 62 | { 63 | "path": "calendarExample", 64 | "name": "calendarExample", 65 | "meta": { "title": "日历","icon": "el-icon-date" } 66 | } 67 | ] 68 | }, 69 | { 70 | "path": "/map", 71 | "name": "map", 72 | "meta": { 73 | "title": "地图", 74 | "icon": "el-icon-location" 75 | }, 76 | "component":"Layout", 77 | "children": [ 78 | { 79 | "path": "mapExample", 80 | "name": "mapExample", 81 | "meta": { "title": "地图","icon": "el-icon-location" } 82 | } 83 | ] 84 | } 85 | ] 86 | } 87 | -------------------------------------------------------------------------------- /express/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var db = require("../config/db"); 4 | const jwt = require('jsonwebtoken') 5 | const token = require("../config/token") 6 | var data={data:'',meta:{code:'200',message:''}} 7 | /* GET users listing. */ 8 | router.post('/add', function(req, res, next) { 9 | let username = req.body.username; 10 | let password = req.body.password; 11 | db.query("SELECT username FROM users where username=(?)",[username],function(err,rows){ 12 | console.log(err,rows) 13 | if(rows.length>0){ 14 | data={data:'',meta:{code:'500',message:'用户名存在'}} 15 | res.send(data) 16 | }else{ 17 | db.query("INSERT INTO `users` (`username`,`password`) VALUES (?,?)",[username,password],function(err,rows){ 18 | data={data:'',meta:{code:'200',message:'注册成功'}} 19 | res.send(data) 20 | }); 21 | } 22 | }); 23 | 24 | }); 25 | router.post('/login', function(req, res, next) { 26 | console.log(req.headers) 27 | let username = req.body.username; 28 | let password = req.body.password; 29 | db.query("SELECT * FROM users where username=(?)",[username],function(err,rows){ 30 | if(rows.length>0 && rows[0].password == password){ 31 | // 输出签发的 Token 32 | data={data:token.createToken(req.body),meta:{code:200,message:'登录成功'}} 33 | res.send(data) 34 | }else{ 35 | data.meta={data:'',code:500,message:'用户名或密码错误'} 36 | res.send(data) 37 | } 38 | }); 39 | 40 | }); 41 | router.post('/city', function(req, res, next) { 42 | let accesstoken = req.headers['access_token']; 43 | let datatoken = token.decodeToken(accesstoken) 44 | db.query("UPDATE `users` SET city='"+req.body.city+"',rectangle='"+req.body.rectangle+"' where username='"+datatoken.decoded.name+"'",[],function(err,rows){ 45 | console.log(rows) 46 | data={data:'',meta:{code:'200',message:''}} 47 | res.send(data) 48 | }); 49 | 50 | }); 51 | router.get('/usercity', function(req, res, next) { 52 | db.query("SELECT * FROM `users`",[],function(err,rows){ 53 | let rowdata =[]; 54 | rows.forEach(r=>{ 55 | if(r.city){ 56 | rowdata.push({'city':r.city,'rectangle':r.rectangle}) 57 | } 58 | 59 | }) 60 | data={data:rowdata,meta:{code:'200',message:''}} 61 | res.send(data) 62 | }); 63 | 64 | }); 65 | module.exports = router; 66 | -------------------------------------------------------------------------------- /vue-admin/src/store/modules/app.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | const app = { 4 | state: { 5 | pageOpenedList: [{ 6 | meta:{ title: "首页",icon: "" }, 7 | path: '/home', 8 | name: 'homeindex' 9 | }], 10 | }, 11 | mutations: { 12 | removeTag (state, tags) { 13 | console.log(tags) 14 | state.pageOpenedList.map((item, index) => { 15 | if (item.name ===tags.tag) { 16 | state.pageOpenedList.splice(index, 1); 17 | tags._this.$router.push({name:state.pageOpenedList[index-1].name}); 18 | } 19 | }); 20 | localStorage.pageOpenedList = JSON.stringify(state.pageOpenedList); 21 | }, 22 | pageOpenedList (state, get) { 23 | let openedPage = state.pageOpenedList[get.index]; 24 | if (get.argu) { 25 | openedPage.argu = get.argu; 26 | } 27 | if (get.query) { 28 | openedPage.query = get.query; 29 | } 30 | state.pageOpenedList.splice(get.index, 1, openedPage); 31 | localStorage.pageOpenedList = JSON.stringify(state.pageOpenedList); 32 | }, 33 | clearAllTags (state) { 34 | state.pageOpenedList.splice(1); 35 | localStorage.pageOpenedList = JSON.stringify(state.pageOpenedList); 36 | }, 37 | clearOtherTags (state, vm) { 38 | let currentName = vm.$route.name; 39 | let currentIndex = 0; 40 | state.pageOpenedList.forEach((item, index) => { 41 | if (item.name === currentName) { 42 | currentIndex = index; 43 | } 44 | }); 45 | if (currentIndex === 0) { 46 | state.pageOpenedList.splice(1); 47 | } else { 48 | state.pageOpenedList.splice(currentIndex + 1); 49 | state.pageOpenedList.splice(1, currentIndex - 1); 50 | } 51 | localStorage.pageOpenedList = JSON.stringify(state.pageOpenedList); 52 | }, 53 | setOpenedList (state) { 54 | state.pageOpenedList = localStorage.pageOpenedList ? JSON.parse(localStorage.pageOpenedList) :state.pageOpenedList; 55 | }, 56 | increateTag (state, tagObj) { 57 | console.log(state, tagObj) 58 | state.pageOpenedList.push(tagObj); 59 | localStorage.pageOpenedList = JSON.stringify(state.pageOpenedList); 60 | } 61 | 62 | } 63 | }; 64 | 65 | export default app; 66 | -------------------------------------------------------------------------------- /vue-admin/src/styles/sidebar.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | // 主体区域 3 | .main-container { 4 | min-height: 100%; 5 | transition: margin-left 0.28s; 6 | margin-left: 180px; 7 | } // 侧边栏 8 | .sidebar-container { 9 | transition: width 0.28s; 10 | width: 180px!important; 11 | height: 100%; 12 | position: fixed; 13 | top: 0; 14 | bottom: 0; 15 | left: 0; 16 | z-index: 1001; 17 | a { 18 | display: inline-block; 19 | width: 100%; 20 | } 21 | .svg-icon { 22 | margin-right: 16px; 23 | } 24 | .el-menu { 25 | border: none; 26 | width: 100%; 27 | } 28 | } 29 | .hideSidebar { 30 | .sidebar-container,.sidebar-container .el-menu { 31 | width: 36px!important; 32 | // overflow: inherit; 33 | } 34 | .main-container { 35 | margin-left: 36px; 36 | } 37 | } 38 | .hideSidebar { 39 | .submenu-title-noDropdown { 40 | padding-left: 10px!important; 41 | position: relative; 42 | span { 43 | height: 0; 44 | width: 0; 45 | overflow: hidden; 46 | visibility: hidden; 47 | transition: opacity .3s cubic-bezier(.55, 0, .1, 1); 48 | opacity: 0; 49 | display: inline-block; 50 | } 51 | &:hover { 52 | span { 53 | display: block; 54 | border-radius: 3px; 55 | z-index: 1002; 56 | width: 140px; 57 | height: 56px; 58 | visibility: visible; 59 | position: absolute; 60 | right: -145px; 61 | text-align: left; 62 | text-indent: 20px; 63 | top: 0px; 64 | background-color: $subMenuBg!important; 65 | opacity: 1; 66 | } 67 | } 68 | } 69 | .el-submenu { 70 | &>.el-submenu__title { 71 | padding-left: 10px!important; 72 | &>span { 73 | display: none; 74 | } 75 | .el-submenu__icon-arrow { 76 | display: none; 77 | } 78 | } 79 | .nest-menu { 80 | .el-submenu__icon-arrow { 81 | display: block!important; 82 | } 83 | span { 84 | display: inline-block!important; 85 | } 86 | } 87 | } 88 | } 89 | .nest-menu .el-submenu>.el-submenu__title, 90 | .el-submenu .el-menu-item { 91 | min-width: 180px!important; 92 | background-color: $subMenuBg!important; 93 | &:hover { 94 | background-color: $menuHover!important; 95 | } 96 | } 97 | .el-menu--collapse .el-menu .el-submenu{ 98 | min-width: 180px!important; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /vue-admin/src/views/layout/components/TagsView.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 54 | 55 | 96 | -------------------------------------------------------------------------------- /express/config/token.js: -------------------------------------------------------------------------------- 1 | // var crypto=require("crypto"); 2 | const crypto = require('jsonwebtoken') 3 | const secret = "JWT-TOKEN" 4 | const token={ 5 | createToken:function(obj,timeout){ 6 | // Token 数据 7 | let payload = { 8 | name: obj.username, 9 | admin: true 10 | }; 11 | // 密钥 12 | 13 | // 签发 Token 14 | let tokens = crypto.sign(payload, secret, { expiresIn: 3600}) 15 | // console.log(parseInt(timeout)||0); 16 | // var obj2={ 17 | // data:obj,//payload 18 | // created:parseInt(Date.now()/1000),//token生成的时间的,单位秒 19 | // exp:parseInt(timeout)||10//token有效期 20 | // }; 21 | // //payload信息 22 | // var base64Str=Buffer.from(JSON.stringify(obj2),"utf8").toString("base64"); 23 | // //添加签名,防篡改 24 | // var secret="hel.h-five.com"; 25 | // var hash=crypto.createHmac('sha256',secret); 26 | // hash.update(base64Str); 27 | // var signature=hash.digest('base64'); 28 | return tokens; 29 | }, 30 | decodeToken:function(tokens){ 31 | console.log(tokens) 32 | let res = false; 33 | crypto.verify(tokens, secret , function(err,decoded) { 34 | if(err){ 35 | res = {'flag':false,'decoded':decoded} 36 | }else{ 37 | res = {'flag':true,'decoded':decoded} 38 | } 39 | // try{ 40 | // console.log(err,decoded) 41 | // res = decoded || {}; 42 | // }catch(err){ 43 | // console.log(e); 44 | // } 45 | 46 | }) 47 | return res; 48 | // var decArr=token.split("."); 49 | // if(decArr.length<2){ 50 | // //token不合法 51 | // return false; 52 | // } 53 | // var payload={}; 54 | // //将payload json字符串 解析为对象 55 | // try{ 56 | // payload=JSON.parse(Buffer.from(decArr[0],"base64").toString("utf8")); 57 | // }catch(e){ 58 | // return false; 59 | // } 60 | // //检验签名 61 | // var secret="hel.h-five.com"; 62 | // var hash=crypto.createHmac('sha256',secret); 63 | // hash.update(decArr[0]); 64 | // var checkSignature=hash.digest('base64'); 65 | // return { 66 | // payload:payload, 67 | // signature:decArr[1], 68 | // checkSignature:checkSignature 69 | // } 70 | }, 71 | checkToken:function(token){ 72 | var resDecode=this.decodeToken(token); 73 | if(!resDecode){ 74 | return false; 75 | } 76 | //是否过期 77 | var expState=(parseInt(Date.now()/1000)-parseInt(resDecode.payload.created))>parseInt(resDecode.payload.exp)?false:true; 78 | if(resDecode.signature===resDecode.checkSignature&&expState){ 79 | return true; 80 | } 81 | return false; 82 | } 83 | }; 84 | module.exports=exports=token; -------------------------------------------------------------------------------- /vue-admin/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function (options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders (loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap 41 | }) 42 | }) 43 | } 44 | 45 | // Extract CSS when that option is specified 46 | // (which is the case during production build) 47 | if (options.extract) { 48 | return ExtractTextPlugin.extract({ 49 | use: loaders, 50 | fallback: 'vue-style-loader', 51 | }) 52 | } else { 53 | return ['vue-style-loader'].concat(loaders) 54 | } 55 | } 56 | 57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 58 | return { 59 | css: generateLoaders(), 60 | postcss: generateLoaders(), 61 | less: generateLoaders('less'), 62 | sass: generateLoaders('sass', { indentedSyntax: true }), 63 | scss: generateLoaders('sass'), 64 | stylus: generateLoaders('stylus'), 65 | styl: generateLoaders('stylus') 66 | } 67 | } 68 | 69 | // Generate loaders for standalone style files (outside of .vue) 70 | exports.styleLoaders = function (options) { 71 | const output = [] 72 | const loaders = exports.cssLoaders(options) 73 | 74 | for (const extension in loaders) { 75 | const loader = loaders[extension] 76 | output.push({ 77 | test: new RegExp('\\.' + extension + '$'), 78 | use: loader 79 | }) 80 | } 81 | 82 | return output 83 | } 84 | 85 | exports.createNotifierCallback = () => { 86 | const notifier = require('node-notifier') 87 | 88 | return (severity, errors) => { 89 | if (severity !== 'error') return 90 | 91 | const error = errors[0] 92 | const filename = error.file && error.file.split('!').pop() 93 | 94 | notifier.notify({ 95 | title: packageConfig.name, 96 | message: severity + ': ' + error.name, 97 | subtitle: filename || '', 98 | icon: path.join(__dirname, 'logo.png') 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /vue-admin/src/views/calendar/calendarExample.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 73 | 74 | 76 | -------------------------------------------------------------------------------- /vue-admin/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const path = require('path') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 11 | const portfinder = require('portfinder') 12 | 13 | const HOST = process.env.HOST 14 | const PORT = process.env.PORT && Number(process.env.PORT) 15 | 16 | const devWebpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 19 | }, 20 | // cheap-module-eval-source-map is faster for development 21 | devtool: config.dev.devtool, 22 | 23 | // these devServer options should be customized in /config/index.js 24 | devServer: { 25 | clientLogLevel: 'warning', 26 | historyApiFallback: { 27 | rewrites: [ 28 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, 29 | ], 30 | }, 31 | hot: true, 32 | contentBase: false, // since we use CopyWebpackPlugin. 33 | compress: true, 34 | host: HOST || config.dev.host, 35 | port: PORT || config.dev.port, 36 | open: config.dev.autoOpenBrowser, 37 | overlay: config.dev.errorOverlay 38 | ? { warnings: false, errors: true } 39 | : false, 40 | publicPath: config.dev.assetsPublicPath, 41 | proxy: config.dev.proxyTable, 42 | quiet: true, // necessary for FriendlyErrorsPlugin 43 | watchOptions: { 44 | poll: config.dev.poll, 45 | } 46 | }, 47 | plugins: [ 48 | new webpack.DefinePlugin({ 49 | 'process.env': require('../config/dev.env') 50 | }), 51 | new webpack.HotModuleReplacementPlugin(), 52 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 53 | new webpack.NoEmitOnErrorsPlugin(), 54 | // https://github.com/ampedandwired/html-webpack-plugin 55 | new HtmlWebpackPlugin({ 56 | filename: 'index.html', 57 | template: 'index.html', 58 | inject: true 59 | }), 60 | // copy custom static assets 61 | new CopyWebpackPlugin([ 62 | { 63 | from: path.resolve(__dirname, '../static'), 64 | to: config.dev.assetsSubDirectory, 65 | ignore: ['.*'] 66 | } 67 | ]) 68 | ] 69 | }) 70 | 71 | module.exports = new Promise((resolve, reject) => { 72 | portfinder.basePort = process.env.PORT || config.dev.port 73 | portfinder.getPort((err, port) => { 74 | if (err) { 75 | reject(err) 76 | } else { 77 | // publish the new Port, necessary for e2e tests 78 | process.env.PORT = port 79 | // add port to devServer config 80 | devWebpackConfig.devServer.port = port 81 | 82 | // Add FriendlyErrorsPlugin 83 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 84 | compilationSuccessInfo: { 85 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 86 | }, 87 | onErrors: config.dev.notifyOnErrors 88 | ? utils.createNotifierCallback() 89 | : undefined 90 | })) 91 | 92 | resolve(devWebpackConfig) 93 | } 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /vue-admin/src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | @import './mixin.scss'; 3 | @import './transition.scss'; 4 | @import './element-ui.scss'; 5 | @import './sidebar.scss'; 6 | @import './btn.scss'; 7 | 8 | body { 9 | height: 100%; 10 | -moz-osx-font-smoothing: grayscale; 11 | -webkit-font-smoothing: antialiased; 12 | text-rendering: optimizeLegibility; 13 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; 14 | } 15 | 16 | label { 17 | font-weight: 700; 18 | } 19 | 20 | html { 21 | height: 100%; 22 | box-sizing: border-box; 23 | } 24 | 25 | #app{ 26 | height: 100%; 27 | } 28 | 29 | *, 30 | *:before, 31 | *:after { 32 | box-sizing: inherit; 33 | } 34 | 35 | .no-padding { 36 | padding: 0px !important; 37 | } 38 | 39 | .padding-content { 40 | padding: 4px 0; 41 | } 42 | 43 | a:focus, 44 | a:active { 45 | outline: none; 46 | } 47 | 48 | a, 49 | a:focus, 50 | a:hover { 51 | cursor: pointer; 52 | color: inherit; 53 | text-decoration: none; 54 | } 55 | 56 | div:focus{ 57 | outline: none; 58 | } 59 | 60 | .fr { 61 | float: right; 62 | } 63 | 64 | .fl { 65 | float: left; 66 | } 67 | 68 | .pr-5 { 69 | padding-right: 5px; 70 | } 71 | 72 | .pl-5 { 73 | padding-left: 5px; 74 | } 75 | 76 | .block { 77 | display: block; 78 | } 79 | 80 | .pointer { 81 | cursor: pointer; 82 | } 83 | 84 | .inlineBlock { 85 | display: block; 86 | } 87 | 88 | .clearfix { 89 | &:after { 90 | visibility: hidden; 91 | display: block; 92 | font-size: 0; 93 | content: " "; 94 | clear: both; 95 | height: 0; 96 | } 97 | } 98 | 99 | code { 100 | background: #eef1f6; 101 | padding: 15px 16px; 102 | margin-bottom: 20px; 103 | display: block; 104 | line-height: 36px; 105 | font-size: 15px; 106 | font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif; 107 | a { 108 | color: #337ab7; 109 | cursor: pointer; 110 | &:hover { 111 | color: rgb(32, 160, 255); 112 | } 113 | } 114 | } 115 | 116 | .warn-content{ 117 | background: rgba(66,185,131,.1); 118 | border-radius: 2px; 119 | padding: 16px; 120 | padding: 1rem; 121 | line-height: 1.6rem; 122 | word-spacing: .05rem; 123 | a{ 124 | color: #42b983; 125 | font-weight: 600; 126 | } 127 | } 128 | 129 | //main-container全局样式 130 | .app-container { 131 | padding: 20px; 132 | } 133 | 134 | .components-container { 135 | margin: 30px 50px; 136 | position: relative; 137 | } 138 | 139 | .pagination-container { 140 | margin-top: 30px; 141 | } 142 | 143 | .text-center { 144 | text-align: center 145 | } 146 | 147 | .sub-navbar { 148 | height: 50px; 149 | line-height: 50px; 150 | position: relative; 151 | width: 100%; 152 | text-align: right; 153 | padding-right: 20px; 154 | transition: 600ms ease position; 155 | background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); 156 | .subtitle { 157 | font-size: 20px; 158 | color: #fff; 159 | } 160 | &.draft { 161 | background: #d0d0d0; 162 | } 163 | &.deleted { 164 | background: #d0d0d0; 165 | } 166 | } 167 | 168 | .link-type, 169 | .link-type:focus { 170 | color: #337ab7; 171 | cursor: pointer; 172 | &:hover { 173 | color: rgb(32, 160, 255); 174 | } 175 | } 176 | 177 | .filter-container { 178 | padding-bottom: 10px; 179 | .filter-item { 180 | display: inline-block; 181 | vertical-align: middle; 182 | margin-bottom: 10px; 183 | } 184 | } 185 | 186 | //refine vue-multiselect plugin 187 | .multiselect { 188 | line-height: 16px; 189 | } 190 | 191 | .multiselect--active { 192 | z-index: 1000 !important; 193 | } 194 | -------------------------------------------------------------------------------- /vue-admin/src/views/layout/TagsView.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 93 | 94 | 140 | -------------------------------------------------------------------------------- /vue-admin/src/views/layout/components/Navbar.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 73 | 74 | 117 | 126 | 127 | -------------------------------------------------------------------------------- /vue-admin/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 13 | 14 | const env = require('../config/prod.env') 15 | 16 | const webpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ 19 | sourceMap: config.build.productionSourceMap, 20 | extract: true, 21 | usePostCSS: true 22 | }) 23 | }, 24 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 25 | output: { 26 | path: config.build.assetsRoot, 27 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 29 | }, 30 | plugins: [ 31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 32 | new webpack.DefinePlugin({ 33 | 'process.env': env 34 | }), 35 | new UglifyJsPlugin({ 36 | uglifyOptions: { 37 | compress: { 38 | warnings: false 39 | } 40 | }, 41 | sourceMap: config.build.productionSourceMap, 42 | parallel: true 43 | }), 44 | // extract css into its own file 45 | new ExtractTextPlugin({ 46 | filename: utils.assetsPath('css/[name].[contenthash].css'), 47 | // Setting the following option to `false` will not extract CSS from codesplit chunks. 48 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 49 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 50 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 51 | allChunks: true, 52 | }), 53 | // Compress extracted CSS. We are using this plugin so that possible 54 | // duplicated CSS from different components can be deduped. 55 | new OptimizeCSSPlugin({ 56 | cssProcessorOptions: config.build.productionSourceMap 57 | ? { safe: true, map: { inline: false } } 58 | : { safe: true } 59 | }), 60 | // generate dist index.html with correct asset hash for caching. 61 | // you can customize output by editing /index.html 62 | // see https://github.com/ampedandwired/html-webpack-plugin 63 | new HtmlWebpackPlugin({ 64 | filename: config.build.index, 65 | template: 'index.html', 66 | inject: true, 67 | minify: { 68 | removeComments: true, 69 | collapseWhitespace: true, 70 | removeAttributeQuotes: true 71 | // more options: 72 | // https://github.com/kangax/html-minifier#options-quick-reference 73 | }, 74 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 75 | chunksSortMode: 'dependency' 76 | }), 77 | // keep module.id stable when vendor modules does not change 78 | new webpack.HashedModuleIdsPlugin(), 79 | // enable scope hoisting 80 | new webpack.optimize.ModuleConcatenationPlugin(), 81 | // split vendor js into its own file 82 | new webpack.optimize.CommonsChunkPlugin({ 83 | name: 'vendor', 84 | minChunks (module) { 85 | // any required modules inside node_modules are extracted to vendor 86 | return ( 87 | module.resource && 88 | /\.js$/.test(module.resource) && 89 | module.resource.indexOf( 90 | path.join(__dirname, '../node_modules') 91 | ) === 0 92 | ) 93 | } 94 | }), 95 | // extract webpack runtime and module manifest to its own file in order to 96 | // prevent vendor hash from being updated whenever app bundle is updated 97 | new webpack.optimize.CommonsChunkPlugin({ 98 | name: 'manifest', 99 | minChunks: Infinity 100 | }), 101 | // This instance extracts shared chunks from code splitted chunks and bundles them 102 | // in a separate chunk, similar to the vendor chunk 103 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 104 | new webpack.optimize.CommonsChunkPlugin({ 105 | name: 'app', 106 | async: 'vendor-async', 107 | children: true, 108 | minChunks: 3 109 | }), 110 | 111 | // copy custom static assets 112 | new CopyWebpackPlugin([ 113 | { 114 | from: path.resolve(__dirname, '../static'), 115 | to: config.build.assetsSubDirectory, 116 | ignore: ['.*'] 117 | } 118 | ]) 119 | ] 120 | }) 121 | 122 | if (config.build.productionGzip) { 123 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 124 | 125 | webpackConfig.plugins.push( 126 | new CompressionWebpackPlugin({ 127 | asset: '[path].gz[query]', 128 | algorithm: 'gzip', 129 | test: new RegExp( 130 | '\\.(' + 131 | config.build.productionGzipExtensions.join('|') + 132 | ')$' 133 | ), 134 | threshold: 10240, 135 | minRatio: 0.8 136 | }) 137 | ) 138 | } 139 | 140 | if (config.build.bundleAnalyzerReport) { 141 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 142 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 143 | } 144 | 145 | module.exports = webpackConfig 146 | -------------------------------------------------------------------------------- /vue-admin/src/components/loginpage.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 122 | 123 | -------------------------------------------------------------------------------- /vue-admin/src/views/table/tabletwo.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 203 | 217 | 218 | -------------------------------------------------------------------------------- /vue-admin/src/views/login/login.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 126 | 127 | -------------------------------------------------------------------------------- /express/public/static/js/manifest.205e82be084d477ab0df.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/bootstrap c405d97a8ada7d592c95"],"names":["parentJsonpFunction","window","chunkIds","moreModules","executeModules","moduleId","chunkId","result","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","shift","__webpack_require__","s","installedModules","3","exports","module","l","e","installedChunkData","Promise","resolve","promise","reject","head","document","getElementsByTagName","script","createElement","type","charset","async","timeout","nc","setAttribute","src","p","0","setTimeout","onScriptComplete","onerror","onload","clearTimeout","chunk","Error","undefined","appendChild","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","n","__esModule","object","property","oe","err","console","error"],"mappings":"aACA,IAAAA,EAAAC,OAAA,aACAA,OAAA,sBAAAC,EAAAC,EAAAC,GAIA,IADA,IAAAC,EAAAC,EAAAC,EAAAC,EAAA,EAAAC,KACQD,EAAAN,EAAAQ,OAAoBF,IAC5BF,EAAAJ,EAAAM,GACAG,EAAAL,IACAG,EAAAG,KAAAD,EAAAL,GAAA,IAEAK,EAAAL,GAAA,EAEA,IAAAD,KAAAF,EACAU,OAAAC,UAAAC,eAAAC,KAAAb,EAAAE,KACAY,EAAAZ,GAAAF,EAAAE,IAIA,IADAL,KAAAE,EAAAC,EAAAC,GACAK,EAAAC,QACAD,EAAAS,OAAAT,GAEA,GAAAL,EACA,IAAAI,EAAA,EAAYA,EAAAJ,EAAAM,OAA2BF,IACvCD,EAAAY,IAAAC,EAAAhB,EAAAI,IAGA,OAAAD,GAIA,IAAAc,KAGAV,GACAW,EAAA,GAIA,SAAAH,EAAAd,GAGA,GAAAgB,EAAAhB,GACA,OAAAgB,EAAAhB,GAAAkB,QAGA,IAAAC,EAAAH,EAAAhB,IACAG,EAAAH,EACAoB,GAAA,EACAF,YAUA,OANAN,EAAAZ,GAAAW,KAAAQ,EAAAD,QAAAC,IAAAD,QAAAJ,GAGAK,EAAAC,GAAA,EAGAD,EAAAD,QAKAJ,EAAAO,EAAA,SAAApB,GACA,IAAAqB,EAAAhB,EAAAL,GACA,OAAAqB,EACA,WAAAC,QAAA,SAAAC,GAA0CA,MAI1C,GAAAF,EACA,OAAAA,EAAA,GAIA,IAAAG,EAAA,IAAAF,QAAA,SAAAC,EAAAE,GACAJ,EAAAhB,EAAAL,IAAAuB,EAAAE,KAEAJ,EAAA,GAAAG,EAGA,IAAAE,EAAAC,SAAAC,qBAAA,WACAC,EAAAF,SAAAG,cAAA,UACAD,EAAAE,KAAA,kBACAF,EAAAG,QAAA,QACAH,EAAAI,OAAA,EACAJ,EAAAK,QAAA,KAEArB,EAAAsB,IACAN,EAAAO,aAAA,QAAAvB,EAAAsB,IAEAN,EAAAQ,IAAAxB,EAAAyB,EAAA,aAAAtC,EAAA,KAAwEuC,EAAA,wBAA2BvC,GAAA,MACnG,IAAAkC,EAAAM,WAAAC,EAAA,MAEA,SAAAA,IAEAZ,EAAAa,QAAAb,EAAAc,OAAA,KACAC,aAAAV,GACA,IAAAW,EAAAxC,EAAAL,GACA,IAAA6C,IACAA,GACAA,EAAA,OAAAC,MAAA,iBAAA9C,EAAA,aAEAK,EAAAL,QAAA+C,GAKA,OAfAlB,EAAAa,QAAAb,EAAAc,OAAAF,EAaAf,EAAAsB,YAAAnB,GAEAL,GAIAX,EAAAoC,EAAAtC,EAGAE,EAAAqC,EAAAnC,EAGAF,EAAAsC,EAAA,SAAAlC,EAAAmC,EAAAC,GACAxC,EAAAyC,EAAArC,EAAAmC,IACA7C,OAAAgD,eAAAtC,EAAAmC,GACAI,cAAA,EACAC,YAAA,EACAC,IAAAL,KAMAxC,EAAA8C,EAAA,SAAAzC,GACA,IAAAmC,EAAAnC,KAAA0C,WACA,WAA2B,OAAA1C,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAL,EAAAsC,EAAAE,EAAA,IAAAA,GACAA,GAIAxC,EAAAyC,EAAA,SAAAO,EAAAC,GAAsD,OAAAvD,OAAAC,UAAAC,eAAAC,KAAAmD,EAAAC,IAGtDjD,EAAAyB,EAAA,KAGAzB,EAAAkD,GAAA,SAAAC,GAA8D,MAApBC,QAAAC,MAAAF,GAAoBA","file":"static/js/manifest.205e82be084d477ab0df.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t3: 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData === 0) {\n \t\t\treturn new Promise(function(resolve) { resolve(); });\n \t\t}\n\n \t\t// a Promise means \"currently loading\".\n \t\tif(installedChunkData) {\n \t\t\treturn installedChunkData[2];\n \t\t}\n\n \t\t// setup Promise in chunk cache\n \t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t});\n \t\tinstalledChunkData[2] = promise;\n\n \t\t// start chunk loading\n \t\tvar head = document.getElementsByTagName('head')[0];\n \t\tvar script = document.createElement('script');\n \t\tscript.type = \"text/javascript\";\n \t\tscript.charset = 'utf-8';\n \t\tscript.async = true;\n \t\tscript.timeout = 120000;\n\n \t\tif (__webpack_require__.nc) {\n \t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t}\n \t\tscript.src = __webpack_require__.p + \"static/js/\" + chunkId + \".\" + {\"0\":\"5e11e9578be5142dfe25\"}[chunkId] + \".js\";\n \t\tvar timeout = setTimeout(onScriptComplete, 120000);\n \t\tscript.onerror = script.onload = onScriptComplete;\n \t\tfunction onScriptComplete() {\n \t\t\t// avoid mem leaks in IE.\n \t\t\tscript.onerror = script.onload = null;\n \t\t\tclearTimeout(timeout);\n \t\t\tvar chunk = installedChunks[chunkId];\n \t\t\tif(chunk !== 0) {\n \t\t\t\tif(chunk) {\n \t\t\t\t\tchunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));\n \t\t\t\t}\n \t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t}\n \t\t};\n \t\thead.appendChild(script);\n\n \t\treturn promise;\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"./\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap c405d97a8ada7d592c95"],"sourceRoot":""} -------------------------------------------------------------------------------- /express/views/static/js/manifest.19edb7b930d13abfaf18.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/bootstrap 4cecdb298c8363edbb37"],"names":["parentJsonpFunction","window","chunkIds","moreModules","executeModules","moduleId","chunkId","result","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","shift","__webpack_require__","s","installedModules","3","exports","module","l","e","installedChunkData","Promise","resolve","promise","reject","head","document","getElementsByTagName","script","createElement","type","charset","async","timeout","nc","setAttribute","src","p","0","setTimeout","onScriptComplete","onerror","onload","clearTimeout","chunk","Error","undefined","appendChild","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","n","__esModule","object","property","oe","err","console","error"],"mappings":"aACA,IAAAA,EAAAC,OAAA,aACAA,OAAA,sBAAAC,EAAAC,EAAAC,GAIA,IADA,IAAAC,EAAAC,EAAAC,EAAAC,EAAA,EAAAC,KACQD,EAAAN,EAAAQ,OAAoBF,IAC5BF,EAAAJ,EAAAM,GACAG,EAAAL,IACAG,EAAAG,KAAAD,EAAAL,GAAA,IAEAK,EAAAL,GAAA,EAEA,IAAAD,KAAAF,EACAU,OAAAC,UAAAC,eAAAC,KAAAb,EAAAE,KACAY,EAAAZ,GAAAF,EAAAE,IAIA,IADAL,KAAAE,EAAAC,EAAAC,GACAK,EAAAC,QACAD,EAAAS,OAAAT,GAEA,GAAAL,EACA,IAAAI,EAAA,EAAYA,EAAAJ,EAAAM,OAA2BF,IACvCD,EAAAY,IAAAC,EAAAhB,EAAAI,IAGA,OAAAD,GAIA,IAAAc,KAGAV,GACAW,EAAA,GAIA,SAAAH,EAAAd,GAGA,GAAAgB,EAAAhB,GACA,OAAAgB,EAAAhB,GAAAkB,QAGA,IAAAC,EAAAH,EAAAhB,IACAG,EAAAH,EACAoB,GAAA,EACAF,YAUA,OANAN,EAAAZ,GAAAW,KAAAQ,EAAAD,QAAAC,IAAAD,QAAAJ,GAGAK,EAAAC,GAAA,EAGAD,EAAAD,QAKAJ,EAAAO,EAAA,SAAApB,GACA,IAAAqB,EAAAhB,EAAAL,GACA,OAAAqB,EACA,WAAAC,QAAA,SAAAC,GAA0CA,MAI1C,GAAAF,EACA,OAAAA,EAAA,GAIA,IAAAG,EAAA,IAAAF,QAAA,SAAAC,EAAAE,GACAJ,EAAAhB,EAAAL,IAAAuB,EAAAE,KAEAJ,EAAA,GAAAG,EAGA,IAAAE,EAAAC,SAAAC,qBAAA,WACAC,EAAAF,SAAAG,cAAA,UACAD,EAAAE,KAAA,kBACAF,EAAAG,QAAA,QACAH,EAAAI,OAAA,EACAJ,EAAAK,QAAA,KAEArB,EAAAsB,IACAN,EAAAO,aAAA,QAAAvB,EAAAsB,IAEAN,EAAAQ,IAAAxB,EAAAyB,EAAA,aAAAtC,EAAA,KAAwEuC,EAAA,wBAA2BvC,GAAA,MACnG,IAAAkC,EAAAM,WAAAC,EAAA,MAEA,SAAAA,IAEAZ,EAAAa,QAAAb,EAAAc,OAAA,KACAC,aAAAV,GACA,IAAAW,EAAAxC,EAAAL,GACA,IAAA6C,IACAA,GACAA,EAAA,OAAAC,MAAA,iBAAA9C,EAAA,aAEAK,EAAAL,QAAA+C,GAKA,OAfAlB,EAAAa,QAAAb,EAAAc,OAAAF,EAaAf,EAAAsB,YAAAnB,GAEAL,GAIAX,EAAAoC,EAAAtC,EAGAE,EAAAqC,EAAAnC,EAGAF,EAAAsC,EAAA,SAAAlC,EAAAmC,EAAAC,GACAxC,EAAAyC,EAAArC,EAAAmC,IACA7C,OAAAgD,eAAAtC,EAAAmC,GACAI,cAAA,EACAC,YAAA,EACAC,IAAAL,KAMAxC,EAAA8C,EAAA,SAAAzC,GACA,IAAAmC,EAAAnC,KAAA0C,WACA,WAA2B,OAAA1C,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAL,EAAAsC,EAAAE,EAAA,IAAAA,GACAA,GAIAxC,EAAAyC,EAAA,SAAAO,EAAAC,GAAsD,OAAAvD,OAAAC,UAAAC,eAAAC,KAAAmD,EAAAC,IAGtDjD,EAAAyB,EAAA,KAGAzB,EAAAkD,GAAA,SAAAC,GAA8D,MAApBC,QAAAC,MAAAF,GAAoBA","file":"static/js/manifest.19edb7b930d13abfaf18.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t3: 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData === 0) {\n \t\t\treturn new Promise(function(resolve) { resolve(); });\n \t\t}\n\n \t\t// a Promise means \"currently loading\".\n \t\tif(installedChunkData) {\n \t\t\treturn installedChunkData[2];\n \t\t}\n\n \t\t// setup Promise in chunk cache\n \t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t});\n \t\tinstalledChunkData[2] = promise;\n\n \t\t// start chunk loading\n \t\tvar head = document.getElementsByTagName('head')[0];\n \t\tvar script = document.createElement('script');\n \t\tscript.type = \"text/javascript\";\n \t\tscript.charset = 'utf-8';\n \t\tscript.async = true;\n \t\tscript.timeout = 120000;\n\n \t\tif (__webpack_require__.nc) {\n \t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t}\n \t\tscript.src = __webpack_require__.p + \"static/js/\" + chunkId + \".\" + {\"0\":\"8ccd3d35511bd4c6d3e7\"}[chunkId] + \".js\";\n \t\tvar timeout = setTimeout(onScriptComplete, 120000);\n \t\tscript.onerror = script.onload = onScriptComplete;\n \t\tfunction onScriptComplete() {\n \t\t\t// avoid mem leaks in IE.\n \t\t\tscript.onerror = script.onload = null;\n \t\t\tclearTimeout(timeout);\n \t\t\tvar chunk = installedChunks[chunkId];\n \t\t\tif(chunk !== 0) {\n \t\t\t\tif(chunk) {\n \t\t\t\t\tchunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));\n \t\t\t\t}\n \t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t}\n \t\t};\n \t\thead.appendChild(script);\n\n \t\treturn promise;\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"./\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 4cecdb298c8363edbb37"],"sourceRoot":""} -------------------------------------------------------------------------------- /vue-admin/src/utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiachenpan on 16/11/18. 3 | */ 4 | 5 | export function parseTime(time, cFormat) { 6 | if (arguments.length === 0) { 7 | return null 8 | } 9 | const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' 10 | let date 11 | if (typeof time === 'object') { 12 | date = time 13 | } else { 14 | if (('' + time).length === 10) time = parseInt(time) * 1000 15 | date = new Date(time) 16 | } 17 | const formatObj = { 18 | y: date.getFullYear(), 19 | m: date.getMonth() + 1, 20 | d: date.getDate(), 21 | h: date.getHours(), 22 | i: date.getMinutes(), 23 | s: date.getSeconds(), 24 | a: date.getDay() 25 | } 26 | const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { 27 | let value = formatObj[key] 28 | if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1] 29 | if (result.length > 0 && value < 10) { 30 | value = '0' + value 31 | } 32 | return value || 0 33 | }) 34 | return time_str 35 | } 36 | 37 | export function formatTime(time, option) { 38 | time = +time * 1000 39 | const d = new Date(time) 40 | const now = Date.now() 41 | 42 | const diff = (now - d) / 1000 43 | 44 | if (diff < 30) { 45 | return '刚刚' 46 | } else if (diff < 3600) { // less 1 hour 47 | return Math.ceil(diff / 60) + '分钟前' 48 | } else if (diff < 3600 * 24) { 49 | return Math.ceil(diff / 3600) + '小时前' 50 | } else if (diff < 3600 * 24 * 2) { 51 | return '1天前' 52 | } 53 | if (option) { 54 | return parseTime(time, option) 55 | } else { 56 | return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分' 57 | } 58 | } 59 | 60 | // 格式化时间 61 | export function getQueryObject(url) { 62 | url = url == null ? window.location.href : url 63 | const search = url.substring(url.lastIndexOf('?') + 1) 64 | const obj = {} 65 | const reg = /([^?&=]+)=([^?&=]*)/g 66 | search.replace(reg, (rs, $1, $2) => { 67 | const name = decodeURIComponent($1) 68 | let val = decodeURIComponent($2) 69 | val = String(val) 70 | obj[name] = val 71 | return rs 72 | }) 73 | return obj 74 | } 75 | 76 | /** 77 | *get getByteLen 78 | * @param {Sting} val input value 79 | * @returns {number} output value 80 | */ 81 | export function getByteLen(val) { 82 | let len = 0 83 | for (let i = 0; i < val.length; i++) { 84 | if (val[i].match(/[^\x00-\xff]/ig) != null) { 85 | len += 1 86 | } else { len += 0.5 } 87 | } 88 | return Math.floor(len) 89 | } 90 | 91 | export function cleanArray(actual) { 92 | const newArray = [] 93 | for (let i = 0; i < actual.length; i++) { 94 | if (actual[i]) { 95 | newArray.push(actual[i]) 96 | } 97 | } 98 | return newArray 99 | } 100 | 101 | export function param(json) { 102 | if (!json) return '' 103 | return cleanArray(Object.keys(json).map(key => { 104 | if (json[key] === undefined) return '' 105 | return encodeURIComponent(key) + '=' + 106 | encodeURIComponent(json[key]) 107 | })).join('&') 108 | } 109 | 110 | export function param2Obj(url) { 111 | const search = url.split('?')[1] 112 | if (!search) { 113 | return {} 114 | } 115 | return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}') 116 | } 117 | 118 | export function html2Text(val) { 119 | const div = document.createElement('div') 120 | div.innerHTML = val 121 | return div.textContent || div.innerText 122 | } 123 | 124 | export function objectMerge(target, source) { 125 | /* Merges two objects, 126 | giving the last one precedence */ 127 | 128 | if (typeof target !== 'object') { 129 | target = {} 130 | } 131 | if (Array.isArray(source)) { 132 | return source.slice() 133 | } 134 | Object.keys(source).forEach((property) => { 135 | const sourceProperty = source[property] 136 | if (typeof sourceProperty === 'object') { 137 | target[property] = objectMerge(target[property], sourceProperty) 138 | } else { 139 | target[property] = sourceProperty 140 | } 141 | }) 142 | return target 143 | } 144 | 145 | export function scrollTo(element, to, duration) { 146 | if (duration <= 0) return 147 | const difference = to - element.scrollTop 148 | const perTick = difference / duration * 10 149 | setTimeout(() => { 150 | console.log(new Date()) 151 | element.scrollTop = element.scrollTop + perTick 152 | if (element.scrollTop === to) return 153 | scrollTo(element, to, duration - 10) 154 | }, 10) 155 | } 156 | 157 | export function toggleClass(element, className) { 158 | if (!element || !className) { 159 | return 160 | } 161 | let classString = element.className 162 | const nameIndex = classString.indexOf(className) 163 | if (nameIndex === -1) { 164 | classString += '' + className 165 | } else { 166 | classString = classString.substr(0, nameIndex) + classString.substr(nameIndex + className.length) 167 | } 168 | element.className = classString 169 | } 170 | 171 | export const pickerOptions = [ 172 | { 173 | text: '今天', 174 | onClick(picker) { 175 | const end = new Date() 176 | const start = new Date(new Date().toDateString()) 177 | end.setTime(start.getTime()) 178 | picker.$emit('pick', [start, end]) 179 | } 180 | }, { 181 | text: '最近一周', 182 | onClick(picker) { 183 | const end = new Date(new Date().toDateString()) 184 | const start = new Date() 185 | start.setTime(end.getTime() - 3600 * 1000 * 24 * 7) 186 | picker.$emit('pick', [start, end]) 187 | } 188 | }, { 189 | text: '最近一个月', 190 | onClick(picker) { 191 | const end = new Date(new Date().toDateString()) 192 | const start = new Date() 193 | start.setTime(start.getTime() - 3600 * 1000 * 24 * 30) 194 | picker.$emit('pick', [start, end]) 195 | } 196 | }, { 197 | text: '最近三个月', 198 | onClick(picker) { 199 | const end = new Date(new Date().toDateString()) 200 | const start = new Date() 201 | start.setTime(start.getTime() - 3600 * 1000 * 24 * 90) 202 | picker.$emit('pick', [start, end]) 203 | } 204 | }] 205 | 206 | export function getTime(type) { 207 | if (type === 'start') { 208 | return new Date().getTime() - 3600 * 1000 * 24 * 90 209 | } else { 210 | return new Date(new Date().toDateString()) 211 | } 212 | } 213 | 214 | export function debounce(func, wait, immediate) { 215 | let timeout, args, context, timestamp, result 216 | 217 | const later = function() { 218 | // 据上一次触发时间间隔 219 | const last = +new Date() - timestamp 220 | 221 | // 上次被包装函数被调用时间间隔last小于设定时间间隔wait 222 | if (last < wait && last > 0) { 223 | timeout = setTimeout(later, wait - last) 224 | } else { 225 | timeout = null 226 | // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 227 | if (!immediate) { 228 | result = func.apply(context, args) 229 | if (!timeout) context = args = null 230 | } 231 | } 232 | } 233 | 234 | return function(...args) { 235 | context = this 236 | timestamp = +new Date() 237 | const callNow = immediate && !timeout 238 | // 如果延时不存在,重新设定延时 239 | if (!timeout) timeout = setTimeout(later, wait) 240 | if (callNow) { 241 | result = func.apply(context, args) 242 | context = args = null 243 | } 244 | 245 | return result 246 | } 247 | } 248 | 249 | export function deepClone(source) { 250 | if (!source && typeof source !== 'object') { 251 | throw new Error('error arguments', 'shallowClone') 252 | } 253 | const targetObj = source.constructor === Array ? [] : {} 254 | Object.keys(source).forEach((keys) => { 255 | if (source[keys] && typeof source[keys] === 'object') { 256 | targetObj[keys] = source[keys].constructor === Array ? [] : {} 257 | targetObj[keys] = deepClone(source[keys]) 258 | } else { 259 | targetObj[keys] = source[keys] 260 | } 261 | }) 262 | return targetObj 263 | } 264 | -------------------------------------------------------------------------------- /vue-admin/src/views/table/tableone.vue: -------------------------------------------------------------------------------- 1 | 107 | 108 | -------------------------------------------------------------------------------- /vue-admin/src/views/layout/components/Sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 70 | 90 | 99 | -------------------------------------------------------------------------------- /vue-admin/src/views/home/home.vue: -------------------------------------------------------------------------------- 1 | 75 | 76 | 231 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | # vue-express 8 | [![](https://img.shields.io/travis/iview/iview-admin.svg?style=flat-square)](https://travis-ci.org/iview/iview-admin) 9 | [![vue](https://img.shields.io/badge/vue-2.5.2-brightgreen.svg?style=flat-square)](https://github.com/vuejs/vue) 10 | [![npm](https://img.shields.io/npm/l/express.svg)]() 11 | 12 | 13 | ## 当前版本:v1.0.0 14 | 15 | 16 | 17 | 18 | [在线访问](http://59.110.164.162:3000/#/login) 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 40 | 41 | ## Install 42 | ```bush 43 | // install dependencies 44 | npm install 45 | ``` 46 | ## Run 47 | ### Development 48 | ```bush 49 | npm run dev 50 | ``` 51 | 52 | ### Production(Build) 53 | ```bush 54 | npm run build 55 | `注:dist文件夹拷贝到public` 56 | ``` 57 | 58 | ## 简介 59 |   vue-express是基于Vue.js、express,搭配使用[element](http://element-cn.eleme.io/#/zh-CN) UI组件库形成的一套后台集成解决方案。功能在完善中... 60 | 61 | ## 功能 62 | 63 | - 注册/登录 64 | - 表格 65 | - 单元格合并 66 | - 可拖拽排序 67 | - 表格树 68 | - 日历 69 | 70 | 107 | 108 | 114 | 115 | ## 效果展示 116 | 117 | - 响应式布局首页 118 | ![image](./layout.png) 119 | 120 | - 登录/注册 121 | ![image](./login.gif) 122 | 173 | 174 | 176 | 177 | 178 | 182 | -------------------------------------------------------------------------------- /vue-admin/src/views/layout/Layout.vue: -------------------------------------------------------------------------------- 1 | 77 | 78 | 209 | 210 | 323 | 340 | --------------------------------------------------------------------------------