Browse Source

项目构建

Limengbo 5 years ago
commit
50cc2d81db

+ 10 - 0
.babelrc

@@ -0,0 +1,10 @@
+{
+  "presets": [
+    ["env", {
+      "modules": false,
+      "targets": {
+        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
+      }
+    }]
+  ]
+}

+ 13 - 0
.editorconfig

@@ -0,0 +1,13 @@
+root = true
+
+# 对所有文件生效
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+# 对后缀名为 md 的文件生效
+[*.md]
+trim_trailing_whitespace = false

+ 5 - 0
.eslintignore

@@ -0,0 +1,5 @@
+build/*.js
+config/*.js
+src/assets
+/dist/
+/*.js

+ 31 - 0
.eslintrc.js

@@ -0,0 +1,31 @@
+module.exports = {
+  root: true, // 查找项
+  parserOptions: {
+    parser: 'babel-eslint'
+  },
+  env: {
+    browser: true,
+  },
+  extends: [
+    // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
+    // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
+    'plugin:vue/essential',
+    // https://github.com/standard/standard/blob/master/docs/RULES-en.md
+    'standard'
+  ],
+  // required to lint *.vue files
+  plugins: [
+    'vue'
+  ],
+  // add your custom rules here
+  rules: {
+    // allow async-await
+    'generator-star-spacing': 'off',
+    // allow debugger during development
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
+    // 缩进风格
+    'indent': ['error', 2],
+    // 空行不超过两行
+    'no-multiple-empty-lines': [1, {'max': 2}]
+  }
+}

+ 17 - 0
.gitignore

@@ -0,0 +1,17 @@
+.DS_Store
+node_modules/
+/dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+/test/unit/coverage/
+/test/e2e/reports/
+selenium-debug.log
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln

+ 36 - 0
README.md

@@ -0,0 +1,36 @@
+# webpack前端项目搭建
+
+## 目录结构
+
+```
+webpack-vue
+│   README.md //项目说明
+│   postcss.config.js //css前缀 
+│   package.json //依赖包
+|   index.html //页面
+|   .gitignore //git不提交的文件
+|   .eslintrc.js //eslint配置,规范代码
+|   .eslintignore //eslint不检测的文件 
+|   .editorconfig //编辑器配置
+|   .babelrc //babel配置
+|
+└───src 
+│   │   
+│   │   
+│   │
+│   └───
+│       │   
+│       │   
+│       │   
+│   
+└───config //生产环境和开发环境配置
+|   │   dev.env.js //开发环境
+|   │   prod.env.js //生产环境
+|
+└───build // webpack配置
+    | webpack.base.conf.js //全局配置
+    | webpack.dev.conf.js //开发环境配置
+    | webpack.prod.conf.js //生产环境配置
+```
+
+代码里都有注释,跟vue-cli构建项目差不多,参考[webpack](https://www.webpackjs.com/concepts/)

+ 72 - 0
build/webpack.base.conf.js

@@ -0,0 +1,72 @@
+// 存放dev和prod配置
+const path = require('path');
+const webpack = require('webpack');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const VueLoaderPlugin = require('vue-loader/lib/plugin');
+// 第三方库单独打包
+const AutoDllPlugin = require('autodll-webpack-plugin');
+// 分离css,打包到单独文件
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+
+module.exports = {
+  entry: {
+    bundle: path.resolve(__dirname, '../src/main.js')
+  },
+  output: {
+    path: path.resolve(__dirname, '../dist'),
+    filename: '[name].[hash].js'
+  },
+  module: {
+    rules: [
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader'
+      },
+      {
+        test: /\.js$/,
+        use: ['babel-loader'],
+        include: path.resolve(__dirname + '/src/'),
+        exclude: /node_modules/
+      },
+      {
+        test: /\.(png|svg|jpg|gif)$/,
+        use: [
+          'file-loader'
+        ]
+      },
+      {
+        test: /\.(woff|woff2|eot|ttf|otf)$/,
+        use: [
+          'file-loader'
+        ]
+      }
+    ]
+  },
+  plugins: [
+    new webpack.HashedModuleIdsPlugin(), // 解决vender后面的hash每次都改变
+    new HtmlWebpackPlugin({
+      template: path.resolve(__dirname, '../index.html')
+    }),
+    new AutoDllPlugin({
+      inject: true, // will inject the DLL bundle to index.html
+      debug: true,
+      filename: '[name]_[hash].js',
+      path: './dll',
+      entry: {
+        vendor: ['vue', 'vue-router', 'vuex', 'element-ui']
+      }
+    }), //单独打包第三方库
+    new VueLoaderPlugin(), // 它的职责是将你定义过的其它规则复制并应用到 .vue 文件里相应语言的块
+    new webpack.optimize.SplitChunksPlugin() // 提取公共代码
+  ],
+  resolve: {
+    extensions: ['.js', '.scss', '.css', '.vue'],// 省去后缀
+    alias: {
+        'vue$': 'vue/dist/vue.esm.js', //配置别名 确保webpack可以找到.vue文件
+        "@": path.resolve(__dirname, '../src'),
+        "components": path.resolve(__dirname, '../src/components'),
+        "utils": path.resolve(__dirname + '../src/utils')
+    },
+    modules: ['node_modules']
+  }
+}

+ 47 - 0
build/webpack.dev.conf.js

@@ -0,0 +1,47 @@
+// 存放dev配置
+const merge = require('webpack-merge');
+const webpack = require('webpack');
+const baseConfig = require('./webpack.base.conf');
+const path = require('path');
+
+module.exports = merge(baseConfig, {
+  mode: 'development',
+  devtool: 'inline-source-map',
+  module: {
+    rules: [
+      {
+        test: /\.css$/,
+        use: [
+          'vue-style-loader',
+          'css-loader',
+          'postcss-loader'
+        ]
+      },
+      {
+        test: /\.scss$/,
+        use: [
+          {
+            loader: 'vue-style-loader',
+            options: {
+              publicPath: '../'
+            }
+          },
+          'css-loader',
+          'postcss-loader',
+          'sass-loader',
+        ],
+      },
+    ]
+  },
+  devServer: {
+    contentBase: path.resolve(__dirname, '../dist'), //告诉服务其从哪提供内容
+    hot: true,
+    open: true
+  },
+  plugins: [
+    new webpack.HotModuleReplacementPlugin(), // 开启热更新
+    new webpack.DefinePlugin({
+      'process.env': require('../config/dev.env')
+    }), // 配置请求地址
+  ]
+})

+ 52 - 0
build/webpack.prod.conf.js

@@ -0,0 +1,52 @@
+// 存放prod配置
+const merge = require('webpack-merge');
+const baseConfig = require('./webpack.base.conf');
+const path = require('path');
+const webpack = require('webpack');
+// 清除打包多余文件
+const CleanWebpackPlugin = require('clean-webpack-plugin');
+// 分离css,打包到单独文件
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+// 压缩css
+const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
+
+module.exports = merge(baseConfig, {
+  mode: 'production',
+  devtool: 'source-map',
+  module: {
+    rules: [
+      {
+        test: /\.css$/,
+        use: [
+          MiniCssExtractPlugin.loader,
+          'css-loader',
+          'postcss-loader'
+        ]
+      },
+      {
+        test: /\.scss$/,
+        use: [
+          {
+            loader: MiniCssExtractPlugin.loader,
+            options: {
+              publicPath: '../'
+            }
+          },
+          'css-loader',
+          'postcss-loader',
+          'sass-loader',
+        ],
+      }
+    ]
+  },
+  plugins: [
+    new CleanWebpackPlugin(),
+    new MiniCssExtractPlugin({
+      filename: "css/[name].css",
+      chunkFilename: "css/[id].css"
+    }),
+    new webpack.DefinePlugin({
+      'process.env': require('../config/prod.env')
+    }), // 配置请求地址
+  ]
+})

+ 8 - 0
config/dev.env.js

@@ -0,0 +1,8 @@
+'use strict'
+const merge = require('webpack-merge')
+const prodEnv = require('./prod.env')
+
+module.exports = merge(prodEnv, {
+  NODE_ENV: '"development"',
+  BASE_API: '"http://manage.ai160.com/"',
+})

+ 5 - 0
config/prod.env.js

@@ -0,0 +1,5 @@
+'use strict'
+module.exports = {
+  NODE_ENV: '"production"',
+  BASE_API: '"http://manage.ai160.com/"',
+}

+ 12 - 0
index.html

@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <meta http-equiv="X-UA-Compatible" content="ie=edge">
+  <title>webpack构建vue</title>
+</head>
+<body>
+  <div id="app"></div>
+</body>
+</html>

File diff suppressed because it is too large
+ 9824 - 0
package-lock.json


+ 48 - 0
package.json

@@ -0,0 +1,48 @@
+{
+  "name": "webpack-vue",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.js",
+  "scripts": {
+    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
+    "build": "webpack --config build/webpack.prod.conf.js",
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC",
+  "devDependencies": {
+    "autodll-webpack-plugin": "^0.4.2",
+    "autoprefixer": "^9.5.1",
+    "axios": "^0.18.0",
+    "babel-core": "^6.26.3",
+    "babel-eslint": "^10.0.1",
+    "babel-loader": "^7.1.5",
+    "babel-preset-env": "^1.7.0",
+    "clean-webpack-plugin": "^2.0.1",
+    "css-loader": "^2.1.1",
+    "element-ui": "^2.7.2",
+    "eslint-plugin-vue": "^5.2.2",
+    "file-loader": "^3.0.1",
+    "html-webpack-plugin": "^3.2.0",
+    "mini-css-extract-plugin": "^0.5.0",
+    "node-sass": "^4.11.0",
+    "optimize-css-assets-webpack-plugin": "^5.0.1",
+    "postcss-loader": "^3.0.0",
+    "sass-loader": "^7.1.0",
+    "uglifyjs-webpack-plugin": "^2.1.2",
+    "vue": "^2.6.10",
+    "vue-loader": "^15.7.0",
+    "vue-style-loader": "^4.1.2",
+    "vue-template-compiler": "^2.6.10",
+    "webpack": "^4.29.6",
+    "webpack-cli": "^3.3.0",
+    "webpack-dev-server": "^3.3.0",
+    "webpack-merge": "^4.2.1"
+  },
+  "dependencies": {
+    "nprogress": "^0.2.0",
+    "vue-router": "^3.0.3",
+    "vuex": "^3.1.0"
+  }
+}

+ 5 - 0
postcss.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  plugins: [
+    require('autoprefixer')
+  ]
+}

+ 19 - 0
src/App.vue

@@ -0,0 +1,19 @@
+<template>
+  <div id="app">
+    <router-view/>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'App',
+    data(){
+      return {
+
+      }
+    }
+  }
+</script>
+
+<style>
+</style>

+ 24 - 0
src/api/nav1.js

@@ -0,0 +1,24 @@
+import request from '../utils/request';
+
+export function companyList() {
+  return request({
+    url: '/manageBase/company',
+    method: 'get'
+  })
+}
+
+export function setCompanyList(data) {
+  return request({
+    url: '/manageBase/company',
+    method: 'put',
+    data,
+  })
+}
+
+export function addCompanyList(data) {
+  return request({
+    url: '/manageBase/company',
+    method: 'post',
+    data,
+  })
+}

BIN
src/assets/img/default.jpg


+ 13 - 0
src/components/tab.vue

@@ -0,0 +1,13 @@
+<style scoped>
+</style>
+
+<template>
+  <div class="tab">
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'tab'
+}
+</script>

+ 13 - 0
src/index.css

@@ -0,0 +1,13 @@
+* {
+  padding: 0;
+  margin: 0;
+}
+html, body {
+  width: 100%;
+  height: 100%;
+  font-size: 16px;
+}
+#app {
+  width: 100%;
+  height: 100%;
+}

+ 30 - 0
src/main.js

@@ -0,0 +1,30 @@
+import Vue from 'vue';
+import App from './App.vue';
+import ElementUI from 'element-ui';
+import 'element-ui/lib/theme-chalk/index.css';
+// 路由跳转进度条
+import NProgress from 'nprogress';
+import 'nprogress/nprogress.css';
+import './index.css';
+import router from './router/router';
+import store from './store';
+
+Vue.use(ElementUI);
+
+router.beforeEach((to, from, next) => {
+  NProgress.start();
+  next()
+});
+
+router.afterEach(transition => {
+  NProgress.done();
+});
+
+new Vue({
+  el: '#app',
+  router,
+  store,
+  render: (h) => h(App),
+})
+
+console.log(process.env.BASE_API)

+ 6 - 0
src/pages/index/Index.vue

@@ -0,0 +1,6 @@
+<template>
+  <div>
+      name: admin
+  </div>
+</template>
+

+ 37 - 0
src/pages/layout/Layout.vue

@@ -0,0 +1,37 @@
+<template>
+  <div class="container">
+    <LeftNav/>
+    <div class="right-con">
+      <TopTitle/>
+      <!--<Tabs/>-->
+      <router-view/>
+    </div>
+  </div>
+</template>
+<script>
+import TopTitle from './toptitle/TopTitle';
+import LeftNav from './leftnav/LeftNav';
+import Tabs from './tabs/Tabs';
+
+export default {
+  components: {
+    TopTitle,
+    LeftNav,
+    Tabs
+  },
+}
+</script>
+<style lang="scss">
+  .container {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    .right-con {
+      width: calc(100% - 200px);
+      display: flex;
+      flex-direction: column;
+    }
+  }
+</style>
+
+

+ 53 - 0
src/pages/layout/leftnav/LeftNav.vue

@@ -0,0 +1,53 @@
+<template>
+  <div class="left-nav">
+    <el-menu
+      class="el-menu-vertical-demo"
+      background-color="#324157"
+      text-color="#bfcbd9"
+      active-text-color="#20a0ff"
+      :router=true
+      :default-active="$route.path">
+      <el-menu-item index="/dashboard/index" route="/dashboard/index">
+        <i class="el-icon-menu"></i>
+        <span slot="title">首页</span>
+      </el-menu-item>
+      <el-submenu index="/nav">
+        <template slot="title">
+          <i class="el-icon-location"></i>
+          <span>导航</span>
+        </template>
+        <el-menu-item-group>
+          <el-menu-item index="/nav/nav1" route="/nav/nav1">
+            <span slot="title">导航一</span>
+          </el-menu-item>
+        </el-menu-item-group>
+        <el-menu-item-group>
+          <el-menu-item index="/nav/nav2" route="/nav/nav2">
+            <span slot="title">导航二</span>
+          </el-menu-item>
+        </el-menu-item-group>
+      </el-submenu>
+      <!--
+      <el-menu-item index="/nav/nav1" route="/nav/nav1">
+        <i class="el-icon-menu"></i>
+        <span slot="title">导航一</span>
+      </el-menu-item>
+      <el-menu-item index="/nav/nav2" route="/nav/nav2">
+        <i class="el-icon-setting"></i>
+        <span slot="title">导航二</span>
+      </el-menu-item>
+      -->
+    </el-menu>
+  </div>
+</template>
+<style lang="scss">
+  .left-nav {
+    width: 200px;
+    height: 100%;
+    background-color: #324157;
+    .el-menu {
+      border: none;
+    }
+  }
+</style>
+

+ 126 - 0
src/pages/layout/tabs/Tabs.vue

@@ -0,0 +1,126 @@
+<template>
+  <div class="tabs">
+    <el-tabs v-model="editableTabsValue" type="card" closable @edit="handleTabsEdit">
+      <el-tab-pane
+        :key="item.name"
+        v-for="(item, index) in editableTabs"
+        :label="item.title"
+        :name="item.name"
+      >
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+<script>
+  export default {
+    data() {
+      return {
+        editableTabsValue: '',
+        editableTabs: [],
+      }
+    },
+    methods: {
+      handleTabsEdit(targetName, action) {
+        if (action === 'add') {
+          if (this.editableTabs.length > 2) {
+               this.editableTabs.forEach(item => {
+                //  console.log(item.name)
+                // if (path.indexOf(item.name) == -1) {
+
+                // }
+               })
+          } else {
+            this.editableTabs.push({
+              title: targetName.title,
+              name: targetName.url,
+            });
+            this.editableTabsValue = targetName.url;
+          }
+        }
+        if (action === 'remove') {
+          let tabs = this.editableTabs;
+          let activeName = this.editableTabsValue;
+            tabs.forEach((tab, index) => {
+              if (tab.name === targetName) {
+                let nextTab = tabs[index + 1] || tabs[index - 1];
+                if (nextTab) {
+                  activeName = nextTab.name;
+                }
+              }
+            });
+          this.editableTabsValue = activeName;
+          this.editableTabs = tabs.filter(tab => tab.name !== targetName);
+        }
+      }
+    },
+    watch: {
+      $route(to, from) {
+        const router = this.$router.options.routes;
+        const path = this.$route.path;
+        console.log(path)
+        this.editableTabsValue = path;
+        router.forEach(element => {
+            if(element.children) {
+              element.children.forEach(item => {
+                console.log(item.meta)
+                if (path.indexOf(item.meta.url) !== -1) {
+                  console.log(item.meta)
+                  this.handleTabsEdit(item.meta, 'add')
+                }
+              })
+            }
+        });
+      }
+    },
+    mounted: function () {
+      const router = this.$router.options.routes;
+      const path = this.$route.path;
+      console.log(path)
+      this.editableTabsValue = path;
+      router.forEach(element => {
+          if(element.children) {
+            element.children.forEach(item => {
+              console.log(item.meta)
+              if (path.indexOf(item.meta.url) !== -1) {
+                console.log(item.meta)
+                this.handleTabsEdit(item.meta, 'add')
+              }
+            })
+          }
+      });
+    }
+  }
+</script>
+<style lang="scss">
+  .tabs {
+    white-space: nowrap;
+    overflow: hidden;
+    width: 100%;
+    height: 40px;
+    border-bottom: 1px solid #ccc;
+    padding: 4px 0;
+    box-sizing: border-box;
+    .el-tabs--card {
+      margin: 0;
+      .el-tabs__header {
+        border: none;
+        margin: 0;
+        .el-tabs__item {
+          border: 1px solid #ccc;
+          margin: 0 5px;
+          height: 32px;
+          line-height: 32px;
+        }
+        .el-tabs__nav {
+          border: none;
+        }
+      }
+    }
+    .el-tabs__item.is-active {
+      background-color: #42b983;
+      color: #fff;
+      border-color: #42b983;
+    }
+  }
+</style>
+

+ 78 - 0
src/pages/layout/toptitle/TopTitle.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="top-title">
+    <el-breadcrumb separator="/">
+      <el-breadcrumb-item :to="item.url" v-for="item of breadListLast" :key="item.url">
+        {{item.title}}
+      </el-breadcrumb-item>
+    </el-breadcrumb>
+    <el-dropdown>
+      <div class="avator">
+        <img src="../../../assets/img/default.jpg" alt="">
+      </div>
+      <el-dropdown-menu slot="dropdown">
+        <el-dropdown-item>退出登录</el-dropdown-item>
+      </el-dropdown-menu>
+    </el-dropdown>
+  </div>
+</template>
+<style lang="scss">
+  .top-title {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    width: 100%;
+    height: 50px;
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
+    background: #eff2f7;
+    padding: 0 20px;
+    box-sizing: border-box;
+    .avator {
+      width: 36px;
+      height: 36px;
+      border-radius: 50%;
+      overflow: hidden;
+      img {
+        width:  100%;
+        height: 100%;
+      }
+    }
+  }
+</style>
+<script>
+export default {
+    //面包屑解决方案,此方法只适用于面包屑与路由显示顺序一致,例如path:01/02/03 面包屑也是01/02/03
+    data() {
+      return {
+        breadListLast: []
+      };
+    },
+    methods: {
+      loadChange() {
+        const router = this.$router.options.routes;
+        router.forEach(element => {
+          if(element.children) {
+            element.children.forEach(item => {
+              if (this.$route.path.indexOf(item.meta.url) !== -1) {
+                console.log(item.meta)
+                this.breadListLast.push(item.meta);
+                if(this.breadListLast.length > 1) {
+                  this.breadListLast.shift();
+                }
+              }
+            })
+          }
+        });
+      }
+    },
+    watch: {
+      $route(to, from) {
+        this.loadChange()
+      }
+    },
+    //页面挂载之后,解析路由,给出面包屑,路由里面一定要含有breadCom组件的path
+    mounted: function () {
+      this.loadChange()
+    }
+}
+</script>
+

+ 121 - 0
src/pages/login/Login.vue

@@ -0,0 +1,121 @@
+<template>
+  <div class="login-container">
+    <el-form :model="loginForm" status-icon :rules="rules" ref="loginForm" autocomplete="off" class="login-form">
+      <h3 class="title">登录</h3>
+      <el-form-item prop="userName" class="el-form-item">
+        <el-input v-model="loginForm.userName" name="userName" type="text" autocomplete="off" placeholder="账号"/>
+      </el-form-item>
+      <el-form-item prop="password" class="el-form-item">
+        <el-input type="password" v-model="loginForm.password" autocomplete="off" show-password placeholder="密码"/>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="submitForm('loginForm')">登录</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+  import { mapGetters } from 'vuex'
+  export default {
+    name: 'Login',
+    data() {
+      var checkUserName = (rule, value, callback) => {
+        if (!value) {
+          return callback(new Error('账号不能为空'));
+        }else {
+          callback();
+        }
+      };
+      var validatePass = (rule, value, callback) => {
+        if (value === '') {
+          callback(new Error('请输入密码'));
+        } else {
+          callback();
+        }
+      };
+      return {
+        loginForm: {
+          userName: '',
+          password: ''
+        },
+        rules: {
+          userName: [
+            { validator: checkUserName, trigger: 'blur' }
+          ],
+          password: [
+            { validator: validatePass, trigger: 'blur' }
+          ]
+        }
+      };
+    },
+    computed: {
+      // 使用对象展开运算符将 getter 混入 computed 对象中
+      ...mapGetters([
+        'flag'
+      ])
+    },
+    methods: {
+      submitForm(formName) {
+        this.$refs[formName].validate((valid) => {
+          if (valid) {
+            // console.log(this.loginForm.userName);
+            // const userName = this.loginForm.userName;
+            // const password = this.loginForm.password;
+            this.$store.dispatch('Login', this.loginForm).then(() => {
+              alert('登录成功');
+              this.$router.push({ path: '/nav/nav1' })
+            }).catch(() => {
+              alert('登录失败');
+            })
+            //alert('submit!');
+          } else {
+            console.log('error submit!!');
+            return false;
+          }
+        });
+      },
+      // resetForm(formName) {
+      //   alert(flag);
+      // }
+    }
+  }
+</script>
+
+<style lang="scss">
+  .login-container {
+    width: 100%;
+    height: 100%;
+    background: #2d3a4b;
+    .login-form {
+      position: absolute;
+      left: 0;
+      right: 0;
+      width: 520px;
+      max-width: 100%;
+      padding: 35px 35px 15px 35px;
+      margin: 120px auto;
+      text-align: center;
+      box-sizing: border-box;
+      .title {
+        margin-bottom:30px;
+        color: #fff;
+      }
+      .el-form-item__label {
+        color:#fff;
+      }
+      .el-input input{
+        background: transparent;
+        border: 0px;
+        -webkit-appearance: none;
+        border-radius: 0px;
+        padding: 12px 5px 12px 15px;
+        border: 1px solid rgba(255, 255, 255, 0.1);
+        background: rgba(0, 0, 0, 0.1);
+        border-radius: 5px;
+        color: #fff;
+        height: 47px;
+      }
+    }
+  }
+</style>

+ 120 - 0
src/pages/nav1/Nav1.vue

@@ -0,0 +1,120 @@
+<template>
+  <div>
+     <el-button type="primary" @click="addEdit">增加</el-button>
+    <el-table
+      :data="tableData"
+      style="width: 100%">
+      <el-table-column
+        label="id">
+        <template slot-scope="scope">
+          <span style="margin-left: 10px">{{ scope.row.id }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="姓名">
+        <template slot-scope="scope">
+          <span style="margin-left: 10px">{{ scope.row.name }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
+          <el-button
+            size="mini"
+            type="danger"
+            @click="handleDelete(scope.$index, scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-dialog
+      :title="title"
+      :visible.sync="dialogVisible"
+      width="50%"
+      center>
+      <el-form :model="form" ref="ruleForm">
+        <el-form-item label="姓名" prop="name" :label-width="formLabelWidth" :rules="{required: true, message: '姓名不能为空'}">
+          <el-input v-model="form.name" autocomplete="off"></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="setTable('ruleForm')">确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import { companyList, setCompanyList, addCompanyList } from '../../api/nav1.js';
+  import { mapGetters } from 'vuex';
+  export default {
+    data() {
+      return {
+        tableData: [],
+        dialogVisible: false,
+        form: {
+          name: '',
+          status:'DEL'
+        },
+        formLabelWidth: '120px',
+        setIndex: '',
+        title: '',
+        type: ''
+      }
+    },
+    methods: {
+      handleEdit(index, row) {
+        this.dialogVisible = true;
+        this.setIndex = index;
+        this.form.id = row.id;
+        this.title = '编辑';
+        this.type = 'set';
+        console.log(index, row);
+      },
+      handleDelete(index, row) {
+        console.log(index, row);
+      },
+      setTable(formName) {
+        this.$refs[formName].validate((valid) => {
+          if (valid) {
+            console.log(this.form)
+            const form = this.form;
+            if(this.type == 'add') {
+              addCompanyList(form).then(res => {
+                companyList().then(res => {
+                  if(res.code == 200) {
+                    this.tableData = res.data;
+                  }
+                })
+              })
+            }else {
+              const index = this.setIndex;
+              setCompanyList(form).then(res => {
+                this.tableData[index].name = form.name;
+              });
+            }
+            this.dialogVisible = false;
+          } else {
+            console.log('error submit!!');
+            return false;
+          }
+        });
+      },
+      addEdit() {
+        this.dialogVisible = true;
+        this.title = '增加';
+        this.type = 'add';
+      }
+    },
+    created () {
+      companyList().then(res => {
+        if(res.code == 200) {
+          this.tableData = res.data;
+        }
+      })
+    }
+  }
+</script>
+

+ 5 - 0
src/pages/nav2/Nav2.vue

@@ -0,0 +1,5 @@
+<template>
+<div>
+nav2
+</div>
+</template>

+ 50 - 0
src/router/router.js

@@ -0,0 +1,50 @@
+import Vue from 'vue';
+import Router from 'vue-router';
+import tab from 'components/tab';
+import Login from  '@/pages/login/Login';
+import Layout from  '@/pages/layout/Layout';
+Vue.use(Router)
+
+export default new Router({
+  // mode: 'history',
+  // base: process.env.BASE_URL,
+  routes: [
+    {
+      path: '/login',
+      name:'login',
+      component: Login,
+    },
+    {
+      path: '/',
+      name:'layout',
+      component: Layout,
+      redirect: '/login',
+      hidden: true
+    },
+    {
+      path: '/dashboard',
+      component: Layout,
+      children: [{
+        path: 'index',
+        name:'Index',
+        meta: { title: '首页', url: '/dashboard/index' },
+        component: () => import('@/pages/index/Index'),
+      }]
+    },
+    {
+      path: '/nav',
+      component: Layout,
+      children: [{
+        path: 'nav1',
+        name:'Nav1',
+        meta: { title: 'nav1', url: '/nav/nav1' },
+        component: () => import('@/pages/nav1/Nav1'),
+      },{
+        path: 'nav2',
+        name:'Nav2',
+        meta: { title: 'nav2', url: '/nav/nav2' },
+        component: () => import('@/pages/nav2/Nav2'),
+      }]
+    }
+  ]
+})

+ 5 - 0
src/store/getter.js

@@ -0,0 +1,5 @@
+const getters = {
+  flag: state => state.login.flag,
+  data: state => state.navList.data
+}
+export default getters

+ 17 - 0
src/store/index.js

@@ -0,0 +1,17 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import login from './modules/login'
+import navList from './modules/navList'
+import getters from './getter'
+
+Vue.use(Vuex)
+
+const store = new Vuex.Store({
+  modules: {
+    login,
+    navList
+  },
+  getters
+})
+
+export default store

+ 28 - 0
src/store/modules/login.js

@@ -0,0 +1,28 @@
+
+const login = {
+  state: {
+    flag: false
+  },
+  mutations: {
+    SET_FLAG: (state, flag) => {
+      state.flag = flag
+    }
+  },
+  actions: {
+    Login({ commit }, userInfo) {
+      console.log(userInfo)
+      const username = userInfo.userName.trim();
+      const password = userInfo.password.trim();
+      return new Promise((resolve, reject) => {
+        if(username == 'admin' && password == '123456') {
+          resolve();
+          commit('SET_FLAG', true)
+        }else {
+          reject();
+        }
+      })
+    }
+  }
+}
+
+export default login

+ 43 - 0
src/store/modules/navList.js

@@ -0,0 +1,43 @@
+import { companyList, setCompanyList } from '../../api/nav1.js';
+const navList = {
+  state: {
+    data: [],
+  },
+  mutations: {
+    GET_LIST: (state, data) => {
+      state.data = data
+    },
+    SET_LIST: (state, { data, index }) => {
+      state.data[index] = data
+    }
+  },
+  actions: {
+    getCompanyList({ commit }) {
+      return new Promise((resolve, reject) => {
+        companyList().then(res => {
+           if(res.code == 200) {
+            commit('GET_LIST', res.data);
+            resolve(res.data);
+          }
+        }).catch(error => {
+          reject(error);
+        })
+      })
+    },
+    setCompanyList({commit}, { form, index }) {
+      return new Promise((resolve, reject) => {
+        setCompanyList(form).then(res => {
+           if(res.code == 200) {
+             const data = res.data;
+             commit('SET_LIST', { data, index });
+            resolve(res.data);
+          }
+        }).catch(error => {
+          reject(error);
+        })
+      })
+    }
+  }
+}
+
+export default navList

+ 74 - 0
src/utils/request.js

@@ -0,0 +1,74 @@
+import axios from 'axios'
+import { Message, MessageBox } from 'element-ui'
+// import store from '../store'
+// import { getToken } from '@/utils/auth'
+
+// 创建axios实例
+const service = axios.create({
+  baseURL: process.env.BASE_API, // api 的 base_url
+  timeout: 5000 // 请求超时时间
+})
+
+// request拦截器
+service.interceptors.request.use(
+  config => {
+    config.headers['uid'] = 1
+    // if (store.getters.token) {
+    //   config.headers['X-Token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
+    // }
+    return config
+  },
+  error => {
+    // Do something with request error
+    console.log(error) // for debug
+    Promise.reject(error)
+  }
+)
+
+// response 拦截器
+service.interceptors.response.use(
+  response => {
+    /**
+     * code为非200是抛错 可结合自己业务进行修改
+     */
+    const res = response.data
+    if (res.code !== 200) {
+      Message({
+        message: res.message,
+        type: 'error',
+        duration: 5 * 1000
+      })
+
+      // // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
+      // if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
+      //   MessageBox.confirm(
+      //     '你已被登出,可以取消继续留在该页面,或者重新登录',
+      //     '确定登出',
+      //     {
+      //       confirmButtonText: '重新登录',
+      //       cancelButtonText: '取消',
+      //       type: 'warning'
+      //     }
+      //   ).then(() => {
+      //     store.dispatch('FedLogOut').then(() => {
+      //       location.reload() // 为了重新实例化vue-router对象 避免bug
+      //     })
+      //   })
+      // }
+      // return Promise.reject('error')
+    } else {
+      return response.data
+    }
+  },
+  error => {
+    console.log('err' + error) // for debug
+    Message({
+      message: error.message,
+      type: 'error',
+      duration: 5 * 1000
+    })
+    return Promise.reject(error)
+  }
+)
+
+export default service