A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© charcoal 中级黑马   /  2021-5-2 17:51  /  2643 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 charcoal 于 2021-5-10 10:28 编辑

初始化项目

1、全局安装vue-cli vue

2、新建文件夹,在文件下初始化项目

vue init webpack vue-elementui(项目名称)

同样一路回车如下

这里取消了ESLint代码风格检查工具以及单元测试模块,当然也可以选择保留

依赖包管理工具选择了yarn(yarn可以缓存之前下载的模块,可以提高模块加载的速度)

默认选择了vue-router模块

然后回车,下载依赖模块

结束之后如上图所示,cd进入项目,然后npm run dev,项目就可以跑起来了,在浏览器中输入localhost:8080即可打开项目



项目初始化完毕,下面来正式开始管理后端的项目搭建


admin项目搭建配置

上面只是简单的完成了项目的初始化,接下来将按照下面的步骤来完成项目的配置

  • 相关三方插件的引入(vuex、axios、element-ui、sass)
  • 项目的基本配置文件配置
  • 路由管理模块vue-router及菜单权限的配置
  • 状态管理模块vuex的配置
  • 请求模块axios的配置

1、相关三方插件的引入(vuex、axios、element-ui、sass)

既然选择了vue全家桶的技术栈,稍大型的项目vuex是肯定要用到的,同时axios必不可少,element-ui(当然还有其它ui框架可以选择,比如说bootscrap)、采用sass样式预处理框架,下面就安装下以上插件

yarn add vuex axios element-ui font-awesome--save

yarn add node-sass  -D

yarn add sass-loader -D

yarn add style-loader -D

以上几行代码用于下载依赖包,安装完成之后,package.json文件如下

sass相关插件放在开发环境下面

在main.js中引入相关插件

上面是各三方插件的引入及注册,状态及路由的配置稍后会详细说明


2、基础配置文件的配置

这里刚一开始要修改的东西不多,暂且只修改以下两处

(1)配置favicon.ico

要配置出自己的框架,一些基础的配置文件还是要修改的,先来说个最简单的好了

拿百度来说,先把标签上的图表弄出来,这个配置要去修改buld ==> webpack.base.conf.js文件

在webpack.base.conf.js文件中引入html-webpack-plugin组件,这个模块可以为html文件中引入的外部资源,我们使用它来添加图标,代码如下

const HtmlWebpackPlugin = require('html-webpack-plugin') //为html文件中引入的外部资源// 配置在module.exports里面plugins: [  new HtmlWebpackPlugin({     template: 'index.html',     favicon: 'favicon.ico',     inject: true   }) ],

将图标生成的favicon.ico文件放在根目录下即可,如何生成favicon.ico请自行百度,很简单

(2)请求的代理

现在还没有配置请求模块axios,配置好了之后请求接口会遇到跨域问题,修改config ==>index.js里面的proxyTable,可配置跨域代理。代码如下

proxyTable: {    '/api/': {    // api为代理接口          target: 'http://localhost:8085/',    // 这里我代理到本地服务          changeOrigin: true     }},

目前基础的配置先修改这两处,如有其它修改的地方在下面会提到,接下来配置项目三大模块(router、axios、vuex)

配置router及菜单权限

在我看来,一个项目最基础的模块就属于路由的配置了,路由不通,页面无法跳转,一切都是浮云,下面就通过路由配置来搭建起项目的基本样子。

在src目录下面新建以下目录和菜单

新建以上文件及文件夹,作用已在上面做标注,重点来说下router.js和home.vue的配置

(1)router.js

首先要保证所有的子页面跳转都在home页面的框架下,路由配置如下

{
    path: '/menu1',
    component: home,
    name: '菜單一',
    iconCls: 'el-icon-message', //图标样式class
    children: [
      {
        path: 'sub1',
        component: sub11,
        name: '子菜單一'
      },
      {
        path: 'sub2',
        component: sub12,
        name: '子菜單二'
      }
    ]
  },
  {
    path: '/menu2',
    component: home,
    name: '菜單二',
    iconCls: 'el-icon-message',
    children: [
      {
        path: 'sub1',
        component: sub21,
        name: '配置管理'
      }
    ]
  }

所有的父组件都为home组件,这样就可以保证子页面的跳转都在home的框架下,详细的配置请看我项目中的配置

(2)home.vue

home组件中的菜单配置是根据路由遍历出来的,如下代码

<!--导航菜单--><aside :class="'menu-expanded'">   <el-menu :default-active="$route.path" ref="bigmenu" class="el-menu-vertical-demo" background-color="#545c64" text-color="#fff" unique-opened                router>      
  <template v-for="(item,index) in $router.options.routes" v-if="!item.hidden && checkContains(item.name)">        
    <el-submenu :index="index+''" v-if="!item.single">         
      <template slot="title"><i :class="item.iconCls"></i>{{item.name}}</template>      
      <el-menu-item v-for="child in item.children" @click="addRouter(child, item.path +'/'+ child.path)" :index="item.path +'/'+ child.path" :key="item.path               +'/'+ child.path" v-if="!child.hidden && checkContains(child.name)">{{child.name}}</el-menu-item>      
     </el-submenu>   
        <router-link v-else v-for="child in item.children" :index="child.path" :key="child.path" :to="child.path">        
      <div @click="addRouter(child)" class="single-menu">{{child.name}}</div>   
      </router-link>     
    </template>
   </el-menu>
</aside>
checkContains方法用来检测后台给的树结构中是否包含此节点,这里用来进行权限控制,同样的,项目代码里都有清晰的注释,可看源码

状态管理模块vuex的配置

状态管理的配置同我之前写的移动端的一样

在src目录下新建store文件夹,如下图

在main.js中引入store/index.js,然后全局注册即可使用,详细请看代码

请求模块axios的配置

请求模块的封装也和之前写的移动端的配置一样


在src目录下新建api和config目录

common.js用于封装请求api


index.js用于封装公共请求方法,详细请看代码


请求封装
  • token 处理
  • 响应处理
  • 请求处理
import axios from 'axios';

import { Message, Msgbox } from 'element3';

import store from 'store/index.js';

// 创建 axios 实例

const service = axios.create({
    // 在请求地址前面加上 baseURL
    baseURL: import.meta.env.VITE_BASE_API,
    // 当前发送跨域请求时携带 cookie
    withCredentials: true,
    timeout: 5000
});

// 请求拦截
service.interceptors.request.use(
    (config) => {
        // 指定请求令牌
        // if (store.getters.token) {
        // // 自定义令牌的字段名为X-Token,根据咱们后台再做修改
        // config.headers["X-Token"] = store.getters.token;
        // }
        config.headers["X-Token"] = "my token";
        return config;
    },
    (error) => {
        // 请求错误的统一处理
        console.log(error); // for debug
        return Promise.reject(error);
    }
);

// 响应拦截器
service.interceptors.response.use(
    /**
     * If you want to get http information such as headers or status
     * Please return  response => response
     */

    /**
     * 通过判断状态码统一处理响应,根据情况修改
     * 同时也可以通过HTTP状态码判断请求结果
     */
    (response) => {
        const res = response.data;

        // 如果状态码不是20000则认为有错误
        if (res.code !== 20000) {
            Message.error({
                message: res.message || "Error",
                duration: 5 * 1000,
            });

            // 50008: 非法令牌; 50012: 其他客户端已登入; 50014: 令牌过期;
            if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
                // 重新登录
                Msgbox.confirm("您已登出, 请重新登录", "确认", {
                    confirmButtonText: "重新登录",
                    cancelButtonText: "取消",
                    type: "warning",
                }).then(() => {
                    store.dispatch("user/resetToken").then(() => {
                        location.reload();
                    });
                });
            }
            return Promise.reject(new Error(res.message || "Error"));
        } else {
            return res;
        }
    },
    (error) => {
        console.log("err" + error); // for debug
        Message({
            message: error.message,
            type: "error",
            duration: 5 * 1000,
        });
        return Promise.reject(error);
    }
);

export default service;

我们要如何组建自己的样式目录
  • var.scss 用于提取颜色值,字体大小值,字体权重值等
  • mixin.scss 写一些公用的样式
目录的重定向问题
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import { viteMockServe } from 'vite-plugin-mock';
import path from 'path';
import vueI18n from '@intlify/vite-plugin-vue-i18n'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueJsx(),
    viteMockServe({ supportTs: false }),
    vueI18n({
      // if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false`
      // compositionOnly: false,

      // you need to set i18n resource including paths !
      include: path.resolve(__dirname, './src/locales/**')
    })
  ],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "src"),
      "comps": path.resolve(__dirname, "src/components"),
      "api": path.resolve(__dirname, "src/api"),
      "views": path.resolve(__dirname, "src/views"),
      "styles": path.resolve(__dirname, "src/styles"),
      "locales": path.resolve(__dirname, "src/locales"),
      "layout": path.resolve(__dirname, "src/layout"),
      "utils": path.resolve(__dirname, "src/utils"),
      "dirs": path.resolve(__dirname, "src/dirs"),
      "plugins": path.resolve(__dirname, "src/plugins"),
      "config": path.resolve(__dirname, "src/config"),
      "router": path.resolve(__dirname, "src/router"),
      "store": path.resolve(__dirname, "src/store"),
      "model": path.resolve(__dirname, "src/model")
    }
  }
});








0 个回复

您需要登录后才可以回帖 登录 | 加入黑马