Создайте фоновую структуру элемента от 0 до 1

Vue.js

Поскольку компания недавно собирается разработать систему управления фоном, я проконсультировался со многими фреймворками vue. Я думаю, что element прост и удобен, поэтому я выбираю его в качестве нашего предпочтительного фреймворка и делюсь им со всеми. Если вы думаете, что есть какой-либо потребность в улучшении, вы можете обсудить это вместе. ,адрес гитхаба. Эта статья относительно длинная, я надеюсь, что студенты смогут терпеливо ее прочитать, если вы не понимаете, вы можете оставить сообщение ниже

один,содержание

содержание
1. Справочник
2. Инициализируйте проект
3. Введение и организация файлового каталога
4. Среда разработки и настройка онлайн-среды
5.vue.config.js конфигурация
6. Введение в ElementUI
7. Введение в маршрутизацию vue-router
8.axios представлен и упакован
9. введение vuex
10. Введение в макет домашней страницы
11. Заключение

Во-вторых, проект инициализации

Сначала установите строительные леса vue глобально, в настоящее время это третья версия vue-cli3.x, вот инструмент управления пакетами npm для установки, если ваша сеть не очень хороша, вы можете сначала установить зеркало Taobao.npm install -g cnpm -registry=https://registry.npm.taobao.org, а затем установить его через cnpm

    cnpm install -g @vue/cli or npm install -g @vue/cli

После установки вы также можете проверить правильность версии (3.x) с помощью этой команды:

    vue --version

После установки строительных лесов начинаем создавать наш проект

    vue create vue-admin-project

Затем появятся два варианта

alt
Выберите второй пункт и продолжите, и выберите функции, которые необходимо настроить, и продолжите, когда закончите, а затем начните генерировать проект

alt
Проект был успешно инициализирован

alt
Затем следуйте инструкциям выше, чтобы запуститьcd appи запускаем локальный серверnpm run serve, когда операция завершится, вам будет предложено вызвать локальный портhttp://localhost:8080, появится страница приветствия, на этом инициализация вашего проекта vue завершена.

alt

3. Введение и организация каталога файлов

Исходный каталог перед сортировкой

|-- vue-admin-project 
  |-- .gitignore            //git项目忽视文件
  |-- babel.config.js       //babel 配置文件
  |-- package-lock.json     //记录安装包的具体版本号
  |-- package.json          //包的类型
  |-- README.md 
  |-- public                //项目打包后的目录
  |   |-- favicon.ico
  |   |-- index.html
  |-- src                   //项目开发目录
      |-- App.vue           //主入口文件
      |-- main.js           //主入口文件
      |-- router.js         //vue-router文件
      |-- store.js          //vuex
      |-- assets //静态文件
         |-- logo.png
      |-- components        //组件存放目录
        |-- HelloWorld.vue
      |-- views             //视图目录
        |-- About.vue
        |-- Home.vue

Организованное оглавление, основные изменения/srcкаталог в папке

|-- vue-admin-project
  |-- .gitignore
  |-- babel.config.js
  |-- package-lock.json
  |-- package.json
  |-- README.md
  |-- public
     |-- favicon.ico
     |-- index.html
  |-- src
      |-- App.vue
      |-- main.js
      |-- assets
         |-- logo.png
      |-- components
         |-- HelloWorld.vue
      |-- router        //路由配置文件夹
         |-- router.js
      |-- store        //状态管理文件夹 
         |-- store.js
      |-- views
         |-- About.vue
         |-- Home.vue

В-четвертых, среда разработки и конфигурация онлайн-среды

Основное отличие vue-cli 3.0x от vue-cli 2.0x в том, что папка структуры проекта упрощена, что тоже приносит много проблем, многие конфигурации нужно настраивать под себя.cofig/Среда разработки и онлайн-среда настраиваются в папке, а 3.0x необходимо настроить самостоятельно.
Сначала настройте среду разработки и создайте новый файл в корневом каталоге проекта..envдокумент.

   NODE_ENV="development"              //开发环境
   BASE_URL="http://localhost:3000/"   //开发环境接口地址

Далее настраиваем онлайн-окружение, а также создаем новый файл в корневой директории проекта.env.prodЭто указывает на производственную среду.

   NODE_ENV="production"              //生产环境
   BASE_URL="url"   //生产环境的地址

Теперь, как мы оцениваем текущую среду в проекте?
мы можем согласноprocess.env.BASE_URLчтобы получить, является ли это онлайн-средой или средой разработки, которая будет использоваться позже

   if(process.env.NODE_ENV='development'){
      console.log( process.env.BASE_URL) //http://localhost:3000/
   }else{
        console.log( process.env.BASE_URL) //url
   }

На данный момент мы успешно настроили среду разработки и онлайн-среду.

Пять, конфигурация vue.config.js

Говоря оvue.config.jsФайл конфигурации проекта, я должен сказать, разница между 3.x и 2.x, элементы конфигурации, связанные с веб-пакетом в 2.x, находятся непосредственно в проекте.build/webpack.base.conf.jsвнутри конфигурации, а 3.x полностью вvue.config.jsВ конфигурации это делает весь проект более лаконичным и понятным, и проект работает быстрее.
Поскольку проект инициализируется безvue.config.jsфайл конфигурации, поэтому нам нужно создать новый в корневом каталоге проектаvue.config.jsэлемент конфигурации.
В этом элементе конфигурации этот проект в основном настраивает три вещи, первая — это псевдоним каталога.alias, другой — автоматически открывать браузер при запуске проекта, а последний — обрабатывать импортированный глобальный файл scss. Есть конечноvue.config.jsКонфигурация намного больше, чем это Заинтересованные студенты могут пойти и посмотретьvue.config.jsКонкретная конфигурация, конкретный код выглядит следующим образом.

   let path=require('path');
   function resolve(dir){
       return path.join(__dirname,dir)
   }
   module.exports = {
       chainWebpack: config => {
           //设置别名
           config.resolve.alias
           .set('@',resolve('src'))
       },
       devServer: {
           open:true  //打开浏览器窗口
       },
       //定义scss全局变量
       css: {
           loaderOptions: {
             sass: {
               data: `@import "@/assets/scss/global.scss";`
             }
           }
         }
   }

Шесть, введение ElementUI

Начать установку ElementUI

    vue add element

Следующие два варианта, первый импортировать все, второй импортировать по требованию, я выбираю первыйFully import, вы можете решить в соответствии с вашим собственным проектом. Далее будет задан вопрос ввести ли scss, тут выбор да, а язык ж-сп.
Далее вам будет предложено, что установка прошла успешно, и на домашней странице проекта есть кнопка в стиле элемента.

Семь, введение в маршрутизацию vue-router

Управление маршрутизацией также является основной частью этого проекта.
1. Импорт файлов

    import Vue from 'vue'
    import Router from 'vue-router'
    import store from '../store/store' //引入状态管理
    import NProgress from 'nprogress' //引入进度条组件 cnpm install nprogress --save
    import 'nprogress/nprogress.css' 
    Vue.use(Router)

2. Маршрутизация ленивой загрузки

    /**
    *@parma {String} name 文件夹名称
    *@parma {String} component 视图组件名称
    */
    const getComponent = (name,component) => () => import(`@/views/${name}/${component}.vue`);

3. Конфигурация маршрутизации

    const myRouter=new Router({
          routes: [
            {
              path: '/',
              redirect: '/home',
              component: getComponent('login','index')
            },
            {
              path: '/login',
              name: 'login',
              component: getComponent('login','index')
            },
            {
              path: '/',
              component:getComponent('layout','Layout'),
              children:[{
                path:'/home',
                name:'home',
                component: getComponent('home','index'),
                meta:{title:'首页'}
              },
              {
                path:'/icon',
                component: getComponent('icons','index'),
                name:'icon',
                meta:{title:'自定义图标'}
              },
              {
                path:'/editor',
                component: getComponent('component','editor'),
                name:'editor',
                meta:{title:'富文本编译器'}
              },
              {
                path:'/countTo',
                component: getComponent('component','countTo'),
                name:'countTo',
                meta:{title:'数字滚动'}
              },
              {
                path:'/tree',
                component: getComponent('component','tree'),
                name:'tree',
                meta:{title:'自定义树'}
              },
              {
                path:'/treeTable',
                component: getComponent('component','treeTable'),
                name:'treeTable',
                meta:{title:'表格树'}
              },
              {
                path:'/treeSelect',
                component: getComponent('component','treeSelect'),
                name:'treeSelect',
                meta:{title:'下拉树'}
              },
              {
                path:'/draglist',
                component: getComponent('draggable','draglist'),
                name:'draglist',
                meta:{title:'拖拽列表'}
              },
              {
                path:'/dragtable',
                component: getComponent('draggable','dragtable'),
                name:'dragtable',
                meta:{title:'拖拽表格'}
              },
              {
                path:'/cricle',
                component: getComponent('charts','cricle'),
                name:'cricle',
                meta:{title:'饼图'}
              },
            ]
            }
          ]
        })

4. В этом проекте есть токен для проверки проблемы с разрешением, поэтому вам нужно оценить, есть ли токен при входе на страницу, если нет, перейти на страницу входа.

    //判断是否存在token
    myRouter.beforeEach((to,from,next)=>{
      NProgress.start()
      if (to.path !== '/login' && !store.state.token) {
         next('/login')     //跳转登录
         NProgress.done()   // 结束Progress
      }
      next()
    })
    myRouter.afterEach(() => {
      NProgress.done() // 结束Progress
    })

5. Экспорт маршрутов

    export default myRouter

Восемь, введение axios и упаковка

1. Я выбираю axios для обработки интерфейса.Поскольку он соответствует спецификации промиса, он вполне может избежать ада обратных вызовов. Теперь приступим к установке

    cnpm install axios -S

2. ВsrcСоздайте новую папку в каталоге с именемapi, Создайте внутри два новых файла, одинapi.js, для интеграции интерфейса другойrequest.js, инкапсулировать запросы axios в соответствии со связанными сервисами.

  • request.js
    1. Введите зависимости
    import axios from "axios";
    import router from "../router/router";
    import {
        Loading 
    } from "element-ui";
    import {messages} from '../assets/js/common.js' //封装的提示文件
    import store from '../store/store' //引入vuex

2. Напишите базовые настройки axios

    axios.defaults.timeout = 60000;                         //设置接口超时时间
    axios.defaults.baseURL = process.env.BASE_URL;          //根据环境设置基础路径
    axios.defaults.headers.post["Content-Type"] =
        "application/x-www-form-urlencoded;charset=UTF-8";  //设置编码
    let loading = null;                                     //初始化loading

3. Напишите перехват запросов, то есть что делать перед запросом интерфейса

    /*
 *请求前拦截
 *用于处理需要请求前的操作
 */
axios.interceptors.request.use(
    config => {
        loading = Loading.service({
            text: "正在加载中......",
            fullscreen: true
        });
        if (store.state.token) {
            config.headers["Authorization"] = "Bearer " + store.state.token;
        }
        return config;
    },
    error => {
        return Promise.reject(error);
    }
);

4. Написать перехват ответа на запрос для обработки операций возврата данных

    /*
     *请求响应拦截
     *用于处理数据返回后的操作
     */
    axios.interceptors.response.use(
        response => {
            return new Promise((resolve, reject) => {
                //请求成功后关闭加载框
                if (loading) {
                    loading.close();
                }
                const res = response.data;
                if (res.err_code === 0) {
                    resolve(res)
                } else{
                    reject(res)
                }
            })
        },
        error => {
            console.log(error)
            //请求成功后关闭加载框
            if (loading) {
                loading.close();
            }
            //断网处理或者请求超时
            if (!error.response) {
                //请求超时
                if (error.message.includes("timeout")) {
                    console.log("超时了");
                    messages("error", "请求超时,请检查互联网连接");
                } else {
                    //断网,可以展示断网组件
                    console.log("断网了");
                    messages("error", "请检查网络是否已连接");
                }
                return;
            }
            const status = error.response.status;
            switch (status) {
                case 500:
                    messages("error", "服务器内部错误");
                    break;
                case 404:
                    messages(
                        "error",
                        "未找到远程服务器"
                    );
                    break;
                case 401:
                    messages("warning", "用户登陆过期,请重新登陆");
                    localStorage.removeItem("token");
                    setTimeout(() => {
                        router.replace({
                            path: "/login",
                            query: {
                                redirect: router.currentRoute.fullPath
                            }
                        });
                    }, 1000);
                    break;
                case 400:
                    messages("error", "数据异常");
                    break;
                default:
                    messages("error", error.response.data.message);
            }
            return Promise.reject(error);
        }
    );

5. Вещи, связанные с запросом, были завершены, и теперь начнем инкапсулировать получение, отправку запроса.

    /*
     *get方法,对应get请求
     *@param {String} url [请求的url地址]
     *@param {Object} params [请求时候携带的参数]
     */
    export function get(url, params) {
        return new Promise((resolve, reject) => {
            axios
                .get(url, {
                    params
                })
                .then(res => {
                    resolve(res);
                })
                .catch(err => {
                    reject(err);
                });
        });
    }
    /*
     *post方法,对应post请求
     *@param {String} url [请求的url地址]
     *@param {Object} params [请求时候携带的参数]
     */
    export function post(url, params) {
        return new Promise((resolve, reject) => {
            axios
                .post(url, params)
                .then(res => {
                    resolve(res);
                })
                .catch(err => {
                    reject(err);
                });
        });
    }
  • api.js
    После инкапсуляции бизнес-логики axios естественно начать, использовать и сначала представитьgetа такжеpostметод
    import {get,post} from './request';

Далее начинаем инкапсулировать интерфейс и экспортировать его

    //登陆
    export const  login=(login)=>post('/api/post/user/login',login)
    //上传
    export const  upload=(upload)=>get('/api/get/upload',upload)

Итак, как мы называем интерфейс? Возьмите целевую страницу в качестве примера.

    import { login } from "@/api/api.js"; //引入login
    /**
    * @oarma {Object} login 接口传递的参数
    */
    login(login)
    .then(res => {
      //成功之后要做的事情
    })
    .catch(err => {
      //出错时要做的事情
    });

Логика, связанная с интерфейсом, была переработана.

Девять, введение vuex

Из-за сложности передачи данных между компонентами в проекте vue официальное введение глобального управления состоянием, то есть vuex, о котором сейчас нужно сказать, vuex может лучше управлять данными и облегчать связь между компонентами.
Теперь создайте четыре новых файла в папке магазина.state.js,mutations.js,getter.js,action.js.

  • state.js
    Состояние — это общедоступное состояние в Vuex.Я рассматриваю состояние как данные всех компонентов, которые используются для сохранения общедоступных данных всех компонентов.
    const state = {
        token: '',//权限验证
        tagsList: [], //打开的标签页个数,
        isCollapse: false, //侧边导航是否折叠
    }
    export default state //导出
  • mutations.js
    Под мутациями я понимаю методы в хранилище, а объект мутации содержит функцию обратного вызова для изменения данных, имя функции официально называется type, первый параметр — состояние, а второй параметр — полезная нагрузка, это пользовательский параметр. значение состояния должно пройти через мутации
    const mutations = {
        //保存token
        COMMIT_TOKEN(state, object) {
            state.token = object.token;
        },
        //保存标签
        TAGES_LIST(state, arr) {
            state.tagsList = arr;
        },
        IS_COLLAPSE(state, bool) {
            state.isCollapse = bool;
        }
    }
    export default mutations
  • getter.js
    Под свойством геттеров я понимаю вычисляемое свойство всех компонентов, то есть вычисляемое свойство. В официальной документации vuex также говорится, что геттеры можно понимать как вычисляемые свойства хранилища.Возвращаемое геттерами значение будет кэшироваться в соответствии с его зависимостями и будет пересчитываться только при изменении значений его зависимостей.
    const getters={
        //你要计算的属性
    }
    export default getters
  • action.js
    действия аналогичны мутациям, за исключением того, что:
    1. действия отправляют мутации вместо прямого изменения состояния
    2.Асинхронные операции можно включать в действия, а асинхронные операции категорически нельзя в мутации
    3. Первым параметром функции обратного вызова в действиях является контекст, который представляет собой объект с теми же свойствами и методами, что и экземпляр хранилища.
    const actions={
    
    }
    export default actions
  • store.js
    store.js — это файл интеграции модуля vuex.Поскольку обновление страницы приведет к потере данных vuex, здесь представлен подключаемый модуль сохранения данных vuex для сохранения данных в состоянии в локальном хранилище.
    Установитьvuex-persistedstate
    npm install vuex-persistedstate --save
    import Vue from 'vue'
    import Vuex from 'vuex'
    import state from "./state";
    import mutations from "./mutations";
    import actions from "./actions";
    import getters from "./getters";
    //引入vuex 数据持久化插件
    import createPersistedState from "vuex-persistedstate"
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state,
      mutations,
      actions,
      getters,
      plugins: [createPersistedState()]
    })

На этом знакомство с vuex завершено, если учащиеся все еще не понимают, они могут прочитать его.vuexдокументация.

10. Введение в макет домашней страницы

Теперь приступаем к оформлению страницы. Во-первых, давайте проанализируем ситуацию с главной страницей.

首页

  1. Боковая панель
  2. верхняя полоса
  3. Раздел контента
    Сначала мыviewСоздайте новый в папкеlayoutпапка, добавьте ещеlayout.vue,а такжеcompententsпапка.
  • Боковая панель
    Создайте новый в папке компонентовAside.vueфайл для реализации логики, связанной с переходами маршрутизации, используя режим маршрутизации меню навигации элемента, если вы не понимаете, вы можете перейти кМеню навигации ElementUIИди проверь.
    <template>
      <div class="aside">
        <el-menu
          :default-active="onRoutes"
          class="el-menu-vertical-demo"
          @open="handleOpen"
          @close="handleClose"
          :collapse="isCollapse"
          active-text-color="#bdb7ff"
          router
        >
          <template v-for="item in items">
            <template v-if="item.subs">
              <el-submenu :index="item.index" :key="item.index">
                <template slot="title">
                  <i :class="item.icon"></i>
                  <span slot="title">{{ item.title }}</span>
                </template>
                <template v-for="subItem in item.subs">
                  <el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
                    <template slot="title">{{ subItem.title }}</template>
                    <el-menu-item
                      v-for="(threeItem,i) in subItem.subs"
                      :key="i"
                      :index="threeItem.index"
                    >{{ threeItem.title }}</el-menu-item>
                  </el-submenu>
                  <el-menu-item v-else :index="subItem.index" :key="subItem.index">{{ subItem.title }}</el-menu-item>
                </template>
              </el-submenu>
            </template>
            <template v-else>
              <el-menu-item :index="item.index" :key="item.index">
                <i :class="item.icon"></i>
                <span slot="title">{{ item.title }}</span>
              </el-menu-item>
            </template>
          </template>
        </el-menu>
      </div>
    </template>
    import { mapState } from "vuex";
    export default {
      data() {
        return {
        //配置目录
          items: [
            {
              icon: "el-icon-edit-outline",
              index: "home",
              title: "系统首页"
            },
            {
              icon: "el-icon-edit-outline",
              index: "icon",
              title: "自定义图标"
            },
            {
              icon: "el-icon-edit-outline",
              index: "component",
              title: "组件",
              subs: [
                {
                  index: "editor",
                  title: "富文本编译器"
                },
                {
                  index: "countTo",
                  title: "数字滚动"
                },
                {
                  index: "trees",
                  title: "树形控件",
                  subs: [
                    {
                      index: "tree",
                      title: "自定义树"
                    },
                    {
                      index: "treeSelect",
                      title: "下拉树"
                    }
                    // ,{
                    //   index:'treeTable',
                    //   title:'表格树',
                    // }
                  ]
                },
              ]
            },
            {
              icon: "el-icon-edit-outline",
              index: "draggable",
              title: "拖拽",
              subs: [
                {
                  index: "draglist",
                  title: "拖拽列表"
                },
                {
                  index: "dragtable",
                  title: "拖拽表格"
                }
              ]
            },
            {
              icon: "el-icon-edit-outline",
              index: "charts",
              title: "图表",
              subs: [
                {
                  index: "cricle",
                  title: "饼图"
                },
              ]
            },
            {
              icon: "el-icon-edit-outline",
              index: "7",
              title: "错误处理",
              subs: [
                {
                  index: "permission",
                  title: "权限测试"
                },
                {
                  index: "404",
                  title: "404页面"
                }
              ]
            },
          ]
        };
      },
      computed: {
        onRoutes() {
          return this.$route.path.replace("/", "");
        },
        ...mapState(["isCollapse"]) //从vuex里面获取菜单是否折叠
      },
      methods: {
        //下拉展开
        handleOpen(key, keyPath) {
          console.log(key, keyPath);
        },
        //下来关闭
        handleClose(key, keyPath) {
          console.log(key, keyPath);
        }
      }
    };
  • верхняя полоса
    существуетview/compententsСоздайте новый в папкеHeader.vue
    <template>
      <div class="head-container clearfix">
        <div class="header-left">
          <showAside :toggle-click="toggleClick"/>
        </div>
        <div class="header-right">
          <div class="header-user-con">
            <!-- 全屏显示 -->
            <div class="btn-fullscreen" @click="handleFullScreen">
              <el-tooltip effect="dark" :content="fullscreen?`取消全屏`:`全屏`" placement="bottom">
                <i class="el-icon-rank"></i>
              </el-tooltip>
            </div>
            <!-- 消息中心 -->
            <div class="btn-bell">
              <el-tooltip effect="dark" :content="message?`有${message}条未读消息`:`消息中心`" placement="bottom">
                <router-link to="/tabs">
                 <i class="el-icon-bell"></i>
                 </router-link>
              </el-tooltip>
              <span class="btn-bell-badge" v-if="message"></span>
            </div>
            <!-- 用户名下拉菜单 -->
            <el-dropdown class="avatar-container" trigger="click">
              <div class="avatar-wrapper">
                <img
                  src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3266090804,66355162&fm=26&gp=0.jpg"
                  class="user-avatar"
                >
                {{username }}<i class="el-icon-caret-bottom"/>
              </div>
              <el-dropdown-menu slot="dropdown" class="user-dropdown">
                <router-link class="inlineBlock" to="/">
                  <el-dropdown-item>首页</el-dropdown-item>
                </router-link>
                <el-dropdown-item>个人设置</el-dropdown-item>
                <el-dropdown-item divided>
                  <span style="display:block;" @click="logout">退出登陆</span>
                </el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
          </div>
        </div>
      </div>
    </template>
    import showAside from "@/components/showAside.vue";//引入了一个侧边栏是否折叠的组件
    export default {
      // name:'header',
      components: {
        showAside
      },
      data() {
        return {
          fullscreen: false,
          name: "linxin",
          message: 2,
          username: "zyh"
        };
      },
      computed: {
        isCollapse: {
          get: function() {
            return this.$store.state.isCollapse;
          },
          set: function(newValue) {
            console.log(newValue);
            this.$store.commit("IS_COLLAPSE", newValue);//提交到vuex
          }
        }
      },
      methods: {
        toggleClick() {
          this.isCollapse = !this.isCollapse;
        },
        // 用户名下拉菜单选择事件
        logout(command) {
          this.$router.push("/login");
        },
        // 全屏事件
        handleFullScreen() {
          let element = document.documentElement;
          if (this.fullscreen) {
            if (document.exitFullscreen) {
              document.exitFullscreen();
            } else if (document.webkitCancelFullScreen) {
              document.webkitCancelFullScreen();
            } else if (document.mozCancelFullScreen) {
              document.mozCancelFullScreen();
            } else if (document.msExitFullscreen) {
              document.msExitFullscreen();
            }
          } else {
            if (element.requestFullscreen) {
              element.requestFullscreen();
            } else if (element.webkitRequestFullScreen) {
              element.webkitRequestFullScreen();
            } else if (element.mozRequestFullScreen) {
              element.mozRequestFullScreen();
            } else if (element.msRequestFullscreen) {
              // IE11
              element.msRequestFullscreen();
            }
          }
          this.fullscreen = !this.fullscreen;
        }
      }
    };

сейчас наsrc/componentsСоздайте новый в папкеshowAside.vueкомпоненты

    <template>
      <div class="clearfix">
        <div class="showAside pull-left" @click="toggleClick">
          <i class="el-icon-menu"></i>
        </div>
      </div>
    </template>
    export default {
      name: "showAside",
      props: {
        toggleClick: {
          type: Function,
          default: null
        }
      }
    };
  • Компонент метки верхней панели навигации
    существуетview/compententsСоздайте новый в папкеTags.vue
    <template>
      <!-- 打开标签的容器 -->
      <div class="tags">
        <ul>
          <li
            class="tags-li"
            v-for="(item,index) in tagsList"
            :key="index"
            :class="{'active': isActive(item.path)}"
          >
            <router-link :to="item.path" class="tags-li-title">{{item.title}}</router-link>
            <span class="tags-li-icon" @click="closeTags(index)">
              <i class="el-icon-close"></i>
            </span>
          </li>
        </ul>
        <div class="tags-close-box">
          <el-dropdown @command="handleCommand">
            <el-button size="mini" type="primary">
              标签选项
              <i class="el-icon-arrow-down el-icon--right"></i>
            </el-button>
            <el-dropdown-menu size="small" slot="dropdown">
              <el-dropdown-item command="closeOther">关闭其他</el-dropdown-item>
              <!-- <el-dropdown-item command="all">关闭所有</el-dropdown-item> -->
            </el-dropdown-menu>
          </el-dropdown>
        </div>
      </div>
    </template>
    import { messages } from "@/assets/js/common.js";
    export default {
      created() {
        //判断标签里面是否有值 有的话直接加载
        if (this.tagsList.length == 0) {
          this.setTags(this.$route);
        }
      },
      computed: {
        //computed 方法里面没有set方法因此不能使用mapState,需要重新定义set方法
        tagsList: {
          get: function() {
            return this.$store.state.tagsList;
          },
          set: function(newValue) {
            this.$store.commit("TAGES_LIST", newValue);
            // this.$store.state.tagsList = newValue;
          }
        }
      },
      watch: {
        //监听路由变化
        $route(newValue, oldValue) {
          this.setTags(newValue);
        }
      },
      methods: {
        //选中的高亮
        isActive(path) {
          return path === this.$route.fullPath;
        },
        handleCommand(command) {
          if (command == "closeOther") {
            // 关闭其他标签
            const curItem = this.tagsList.filter(item => {
              return item.path === this.$route.fullPath;
            });
            this.tagsList = curItem;
          }
        },
        //添加标签
        setTags(route) {
          let isIn = this.tagsList.some(item => {
            //判断标签是否存在
            return item.path === route.fullPath;
          });
          //不存在
          if (!isIn) {
            // 判断当前的标签个数
            if (this.tagsList.length >= 10) {
              messages("warning", "当标签大于10个,请关闭后再打开");
            } else {
              this.tagsList.push({
                title: route.meta.title,
                path: route.fullPath,
                name: route.name
              });
              //存到vuex
              this.$store.commit("TAGES_LIST", this.tagsList);
            }
          }
        },
        closeTags(index) {
          console.log(this.tagsList.length);
          if (this.tagsList.length == 1) {
            messages("warning", "不可全都关闭");
          } else {
            //删除当前
            let tags = this.tagsList.splice(index, 1);
            this.$store.commit("TAGES_LIST", this.tagsList);
          }
        }
      }
    };

следующий вview/compententsСоздайте новый в папкеMain.vue, в основном для объединения верхней панели вкладок навигации и раздела содержимого.

    <template>
        <div class="container">
          <tags />
          <div class="contents">
            <transition name="fade-transform" mode="out-in">
                <router-view></router-view>
            </transition>
          </div>
        </div>
    </template>
    import Tags from './Tags.vue'
    export default {
        components:{
          Tags
        }
    }

Связанные компоненты записываются и обобщаются в компоненте макета.

    <template>
      <div class="wrapper">
        <Aside class="aside-container"/>
        <div class="main-container" :class="isCollapse==true?'container_collapse':''">
          <Header/>
          <Main/>
        </div>
      </div>
    </template>
    import Aside from "./components/Aside.vue";
    import Header from "./components/Header.vue";
    import Main from "./components/Main.vue";
    import { mapState } from "vuex";
    export default {
      name: "Layout",
      components: {
        Aside,
        Header,
        Main
      },
      computed: {
        ...mapState(["isCollapse"])
      }
    };

Пока спланирован макет главной страницы, если не уверены, можете проверить.адрес проекта

11. Заключение

Существуют различные системы управления, и у каждой компании своя бизнес-логика.Эта статья является лишь руководством.Есть еще много областей, которые необходимо пересмотреть и улучшить.Если у студентов есть идеи получше, они могут выступить вперед и надеяться, что все смогут улучшить эту книгу вместе проект.

|-- vue-admin-project
|-- .env
|-- .env.prod
|-- .env.test
|-- .gitignore
|-- babel.config.js
|-- package-lock.json
|-- package.json
|-- README.md
|-- vue.config.js
|-- public
|   |-- favicon.ico
|   |-- index.html
|-- src
    |-- App.vue
    |-- element-variables.scss
    |-- main.js
    |-- api
    |   |-- api.js
    |   |-- request.js
    |-- assets
    |   |-- logo.png
    |   |-- css
    |   |   |-- normalize.css
    |   |   |-- public.css
    |   |-- icon
    |   |   |-- demo.css
    |   |   |-- demo_index.html
    |   |   |-- iconfont.css
    |   |   |-- iconfont.eot
    |   |   |-- iconfont.js
    |   |   |-- iconfont.svg
    |   |   |-- iconfont.ttf
        |   |   |-- iconfont.woff
        |   |   |-- iconfont.woff2
        |   |-- img
        |   |   |-- tou.jpg
        |   |-- js
        |   |   |-- common.js
        |   |-- scss
        |       |-- global.scss
        |-- components
        |   |-- showAside.vue
        |-- plugins
        |   |-- element.js
        |-- router
        |   |-- router.js
        |-- store
        |   |-- actions.js
        |   |-- getters.js
        |   |-- mutations.js
        |   |-- state.js
        |   |-- store.js
        |-- views
            |-- layout
            |   |-- Layout.vue
            |   |-- components
            |       |-- Aside.vue
            |       |-- Header.vue
            |       |-- Main.vue
            |       |-- Tags.vue

Окончательная файловая структура каталога проекта

Ссылки на связанные статьи
Разрешение на создание каркаса фона элемента от 0 до 1
[Оптимизация упаковки] Построить рамку оптимизации фонового фреймворка элемента от 0 до 1