zoukankan      html  css  js  c++  java
  • Vue+webpack项目配置便于维护的目录结构

    新建项目的时候创建合理的目录结构便于后期的维护是很重要

    环境:vue、webpack

    目录结构:

    项目子目录结构

    子目录结构都差不多,主要目录是在src下面操作

    src目录结构

    src/common 目录

    主要用来存放公共的文件

    src/components

    主要用来存放公共的组件

    src/config

    用来存放配置文件,文件目录如下

    src/config/index.js   配置目录入口文件

    import api from './website'
    
    // 当前平台
    export const HOST_PLATFORM = 'WEB'
    // 当前环境
    export const NODE_ENV = process.env.NODE_ENV || 'prod'
    
    // 是否开启监控
    export const MONITOR_ENABLE = true
    
    // 路由默认配置
    export const ROUTER_DEFAULT_CONFIG = {
      // mode: 'history',
      waitForData: true,
      transitionOnLoad: true
    }
    
    // axios 默认配置
    export const AXIOS_DEFAULT_CONFIG = {
      timeout: 20000,
      maxContentLength: 2000,
      headers: {}
    }
    
    // vuex 默认配置
    export const VUEX_DEFAULT_CONFIG = {
      strict: process.env.NODE_ENV !== 'production'
    }
    
    // API 默认配置
    export const API_DEFAULT_CONFIG = {
      baseURL: api,
      // 图标地址
      imgUrl: `${api}/api/system/icon.do?name=`,
      // 菜单图标地址
      menuImgUrl: `${api}/`,
      dicomUrl: `${api}/testDICOM/`,
      // 请求参数格式 json/form-data
      isJSON: true,
      // 请求加载效果, 支持element-ui所有参数配置
      loading: { text: '加载中' },
      // 是否开启mock
      mock: false,
      // 是否开启debug
      debug: false,
      // 定义全局变量
      ippid: 'test'
    }
    
    export const CONSOLE_REQUEST_ENABLE = true // 开启请求参数打印
    export const CONSOLE_RESPONSE_ENABLE = false // 开启响应参数打印
    export const CONSOLE_ROUTER_ENABLE = false // 打印路由信息
    export const CONSOLE_MONITOR_ENABLE = true // 监控记录打印

    src/config/website.js   动态配置ip文件

    /**
     * 动态匹配api接口地址
     */
    const website = [
      {
        web: 'localhost:9000',
        api: '//192.168.0.170:8080/xhhms',
        env: 'dev'
      },
      {
        web: '127.0.0.1:8000',
        api: '//192.168.0.149:8080/xhhms',
        env: 'dev'
      }
    ]
    
    let matchApi = website.filter(item => new RegExp(item.web).test(location.href))
    
    if (matchApi.length > 1) {
      console.error(`${location.href}: 该站点映射了多个api地址${matchApi.map(item => item.api).join(',')},默认选取第一个匹配项`)
    }
    
    export default matchApi[0].api

     src/config/interceptors目录

    拦截器配置

     src/config/interceptors/axios.js

    import router from 'Plugins/router'
    import { CONSOLE_REQUEST_ENABLE, CONSOLE_RESPONSE_ENABLE } from '../index.js'
    import { Toast, Indicator } from 'mint-ui'
    import store from 'Store'
    
    import Qs from 'qs'
    /**
     * 请求拦截器(成功)
     * @param {object} request 请求对象
     * @return {object} request 处理后的请求对象
     */
    export function requestSuccessFunc(request) {
      CONSOLE_REQUEST_ENABLE &&
      console.info('requestInterceptorFunc', `url: ${request.url}`, request)
      // 自定义请求拦截逻辑,可以处理权限,请求发送监控等
      // console.log(request.url)
      // if (localStorage.getItem('token') === null && request.url.indexOf('login') === -1) {
      //   console.log('[*] 当前用户没有登录!!')
      //   router.push('/login')
      //   return false
      // }
      // 登录token携带
      request.headers['X-AUTH-TOKEN'] = localStorage.getItem('token')
    
      // 兼容性写法,如果request里边没得site_code 就用全局site_code
      let publicParams = {
        orgCode: sessionStorage.getItem('orgCode'),
        menuId: sessionStorage.getItem('currentMenuId')
      }
    
      /**
       * @author wucheshi
       * @time 2018-08-13
       * @description 需求变动,网站code从本地siteCodeList 这个字段来
      */
      let siteCodeList = sessionStorage.getItem('siteCodeList')
      // !request.data.site_code && (publicParams = Object.assign({ site_code: store.state.currentSite.code }, publicParams))
      !request.data.site_code && !request.noSiteCode && (publicParams = Object.assign({ site_code: siteCodeList }, publicParams))
    
      /**
       * @author wucheshi
       * @time 2018-08-13
       * @description 单表操作接口不需要传递sitecode
      */
      // 兼容单表操作传递site_code
      // if (request.data.condition && !request.noSiteCode) {
      //   console.log(siteCodeList, 11111)
      //   if (request.data.condition.findIndex(item => item.name === 'site_code') === -1) {
      //     request.data.condition.push({ name: 'site_code', value: siteCodeList })
      //   } else {
      //     request.data.condition.find(item => item.name === 'site_code').value = siteCodeList
      //   }
      // }
    
      let newData
      // 判断是否是formdata类型
      if (Object.prototype.toString.call(request.data) === '[object FormData]') {
        // 合并formdata格式公共参数
        Object.keys(publicParams).forEach(key => {
          request.data.append(key, publicParams[key])
        })
        newData = request.data
      } else {
        // 合并公共参数
        newData = Object.assign(request.data, publicParams)
    
        // 判断是否采用json格式提交参数
        !request.isJSON && (newData = Qs.stringify(newData))
      }
    
      // 不同提交参数方式给不同的字段赋值
      if (request.method.toUpperCase() === 'POST') {
        request.data = newData
      } else if (request.method.toUpperCase() === 'GET') {
        request.params = newData
      }
    
      // 加载效果
      request.loading && Indicator.open(request.loading)
    
      // 输出请求数据
      CONSOLE_REQUEST_ENABLE &&
      console.info(`%c
    请求接口地址:${request.url}
    
    请求接口名称:${request.desc}
    
    请求参数JSON: 
    
    ${JSON.stringify(request.data, '', 2)}
    
    `, 'color: #f60')
    
      return request
    }
    
    /**
     * 请求拦截器(失败)
     * @param {object} requestError 请求报错对象
     * @return {object} 返回promise对象
     */
    export function requestFailFunc(requestError) {
      // 自定义发送请求失败逻辑,断网,请求发送监控等
      return Promise.reject(requestError)
    }
    // 你就是个sx
    /**
     * 响应拦截器(成功)
     * @param {object} responseObj 响应对象
     */
    export function responseSuccessFunc(responseObj) {
      // 自定义响应成功逻辑,全局拦截接口,根据不同业务做不同处理,响应成功监控等
      // console.log(typeof (responseObj.data))
      // // 判断string是否包含 java字段  说明error
      // if (typeof (responseObj.data) === 'string' || responseObj.data.indexOf('java') !== -1) {
      //   console.log('[*] token错误')
      //   this.$router.push('/login')
      // }
      // 加载效果
      Indicator.close()
      // 响应对象
      let resData =
        typeof responseObj.data === 'object'
          ? responseObj.data
          : JSON.parse(responseObj.data)
      let { status, message } = resData
    
      // 输出响应体
      CONSOLE_RESPONSE_ENABLE && console.info(responseObj)
      // 输出返回JSON数据
      CONSOLE_RESPONSE_ENABLE &&
        console.info(`%c
    响应接口地址: ${responseObj.config.url}
    
    响应接口描述: ${responseObj.config.desc}
    
    响应数据JSON:
    
    ${JSON.stringify(resData, '', 2)}
        `, 'color: blue')
    
      // 自定义处理业务逻辑
      if (responseObj.config.customErrorHandle) {
        return resData
      }
    
      // 统一逻辑处理
      switch (+status) {
        case 0: // 常规错误
          Toast(message)
          break
        case 1: // 如果业务成功,直接进成功回调
          return resData
        case 401: // 登录失效
          store.commit('DELETE_USER_INFO')
          router.push({ path: '/login', redirect: router.app._route.fullPath })
          Toast(message)
          break
        default:
          // 业务中还会有一些特殊 code 逻辑,我们可以在这里做统一处理,也可以下方它们到业务层
          // !responseObj.config.noShowDefaultError && GLOBAL.vbus.$emit('global.$dialog.show', resData.msg);
          return Promise.reject(resData)
      }
    }
    
    /**
     * 响应拦截器(失败)
     * @param {object} responseError 响应报错对象
     * @return {object} 返回promise对象
     */
    export function responseFailFunc(responseError) {
      // 响应失败,可根据 responseError.message 和 responseError.response.status 来做监控处理
      // ...
      // 加载效果
      Indicator.close()
      // 错误码处理
      // console.log(responseError.response)
      if (typeof (responseError.response) === 'undefined') {
        return false
      }
      switch (responseError.response.status) {
        case 401:
          console.error('401错误')
          store.commit('DELETE_USER_INFO')
          router.push({ path: '/login', redirect: router.app._route.fullPath })
          store.state.user.username && Toast('登录超时')
          break
        case 403:
          console.error('403错误')
          router.push({ path: '/403' })
          break
        case 500:
          console.error('500错误')
          router.push({ path: '/500' })
          break
      }
      return Promise.reject(responseError)
    }

     src/config/interceptors/index.js

    import {requestSuccessFunc, requestFailFunc, responseSuccessFunc, responseFailFunc} from './axios'
    import {routerBeforeEachFunc} from './router'
    
    export default {
      requestSuccessFunc,
      requestFailFunc,
      responseSuccessFunc,
      responseFailFunc,
      routerBeforeEachFunc
    }

     src/config/interceptors/router.js

    /**
     * 路由beforeach拦截器
     */
    
    import {CONSOLE_ROUTER_ENABLE} from '../index'
    
    export function routerBeforeEachFunc (to, from, next) {
      // 打印路由数据
      CONSOLE_ROUTER_ENABLE && console.info(`%c
    路由to: 
    
    fullPath: ${to.fullPath},
    query: ${JSON.stringify(to.query, '', 2)},
    meta: ${JSON.stringify(to.meta, '', 2)}
    
    路由from: 
    
    fullPath: ${from.fullPath}
    
      `, 'color: green;font-weight: bold;')
    
      // 登录状态验证
      if (to.meta.requireLogin) {
        (localStorage.getItem('token')) ? next() : next({path: '/login', query: { redirect: to.fullPath }})
        return
      }
    
      // 路由重定向
      // if (to.query.route) {
      //   let newQuery = Object.assign({}, to.query)
      //   delete newQuery.route
      //   next({
      //     path: `${to.query.route.indexOf('/') === 0 ? '' : '/'}${to.query.route}`,
      //     query: newQuery
      //   })
      //   return
      // }
      // console.log(to, from)
    
      // 防止死循环
      if (to.fullPath === from.fullPath) return
    
      // 404错误
      if (!to.name) {
        next('/404')
        return
      }
    
      next()
    }

      src/locale目录

    国际化配置,这个百度一下就行

      src/mixin目录  

    引入配置文件,定义部分全局变量,名字自己定义

      src/mixin/index.js

    import Vue from 'vue'
    
    import { API_DEFAULT_CONFIG } from 'Config'
    
    Vue.mixin({
      computed: {
        // 图片根地址
        imgUrl () {
          return API_DEFAULT_CONFIG.imgUrl
        },
        baseUrl () {
          return API_DEFAULT_CONFIG.baseURL
        },
        ippid () {
          return API_DEFAULT_CONFIG.ippid
        },
        dicomUrl () {
          return API_DEFAULT_CONFIG.dicomUrl
        }
      }
    })

      src/pages目录

    主要的页面文件,目录结构主要按照层次结构来分。

    ex:该页面主要跟医生相关,主要包含云搜索(cloud)、个人中心(myCenter)、工作中心(workCenter)、搜索(serach)、同理子层级也同样区分、目录结构如下

    至于公共页面可以放在common文件目录下,也可以摆在文件夹外面。

      src/plugins目录 

    也是配置文件目录

      src/plugins/api.js

    import axios from './axios'
    import _pick from 'lodash/pick'
    import _assign from 'lodash/assign'
    import _isEmpty from 'lodash/isEmpty'
    
    import { assert } from 'Utils/tools'
    import { API_DEFAULT_CONFIG } from 'Config'
    import API_CONFIG from 'Service/api'
    
    class MakeApi {
      constructor (options) {
        this.api = {}
        this.options = Object.assign({}, options)
        this.apiBuilder(options)
      }
    
      apiBuilder ({
        config = {}
      }) {
        Object.keys(config).map(namespace => {
          this._apiSingleBuilder({
            namespace,
            config: config[namespace]
          })
        })
      }
      _apiSingleBuilder ({
        namespace,
        config = {}
      }) {
        config.forEach(api => {
          const { methodsName, desc, params, method, path, mockPath } = api
          let { mock, mockBaseURL, baseURL, debug, isJSON, loading } = this.options
          let url = mock ? (mockBaseURL + mockPath) : (baseURL + path)
          debug && assert(methodsName, `${url} :接口methodsName属性不能为空`)
          debug && assert(url.indexOf('/') === 0, `${url} :接口路径path,首字符应为/`)
    
          Object.defineProperty(this.api, methodsName, {
            value (outerParams, outerOptions) {
              let allowtParam = (outerOptions && outerOptions.allowParams) || {}
              let _data = (outerOptions && outerOptions.isFormData) ? outerParams : _isEmpty(outerParams) ? params : _pick(_assign({}, params, outerParams), Object.keys(Object.assign(params, allowtParam)))
              return axios(_assign({
                url,
                desc,
                method,
                isJSON,
                loading
              }, outerOptions, { data: _data }))
            }
          })
        })
      }
    }
    
    export default new MakeApi({
      config: API_CONFIG,
      ...API_DEFAULT_CONFIG
    })['api']

      src/plugins/axios.js

    import axios from 'axios'
    import {AXIOS_DEFAULT_CONFIG} from 'Config/index'
    import {requestSuccessFunc, requestFailFunc, responseSuccessFunc, responseFailFunc} from 'Config/interceptors/axios'
    
    let axiosInstance = {}
    
    axiosInstance = axios.create(AXIOS_DEFAULT_CONFIG)
    
    // 注入请求拦截
    axiosInstance
      .interceptors.request.use(requestSuccessFunc, requestFailFunc)
    // 注入失败拦截
    axiosInstance
      .interceptors.response.use(responseSuccessFunc, responseFailFunc)
    
    export default axiosInstance

      src/plugins/inject.js

    import axios from './axios'
    import api from './api'
    // GLOBAL.ajax = axios
    export default {
      install: (Vue, options) => {
        Vue.prototype.$api = api
        Vue.prototype.$ajax = axios
        // 需要挂载的都放在这里
      }
    }

      src/plugins/router.js

    import Vue from 'vue'
    import Router from 'vue-router'
    import ROUTES from 'Routes'
    import {ROUTER_DEFAULT_CONFIG} from 'Config/index'
    import {routerBeforeEachFunc} from 'Config/interceptors/router'
    
    Vue.use(Router)
    
    // 注入默认配置和路由表
    let routerInstance = new Router({
      ...ROUTER_DEFAULT_CONFIG,
      routes: ROUTES
    })
    // 注入拦截器
    routerInstance.beforeEach(routerBeforeEachFunc)
    
    export default routerInstance

      src/router目录 

    路由配置文件目录,同理按照页面的层次结构来,结构如下

    我们来看src/router/index.js 和 src/common/index.js 即可

    src/common/index.js

    const routes = [
      {
        path: '/login',
        name: 'Login',
        component: () => import('Pages/login'),
        meta: {
          require: true,
          title: '登录'
        }
      },
      {
        path: '/register',
        name: 'register',
        component: () => import('Pages/register'),
        meta: {
          require: true,
          title: '注册'
        }
      },
      {
        path: '/404',
        name: '404',
        component: () => import('Pages/error/404.vue'),
        meta: {
          require: true,
          title: '404'
        }
      },
      {
        path: '/500',
        name: '500',
        component: () => import('Pages/error/500.vue'),
        meta: {
          require: true,
          title: '500'
        }
      },
      {
        path: '/403',
        name: '403',
        component: () => import('Pages/error/403.vue'),
        meta: {
          require: true,
          title: '403'
        }
      }
    ]
    
    export default routes

    src/router/index.js

    import common from './common'
    import doctor from './doctor'
    import patient from './patient'
    import test from './test'
    
    const route = [
      {
        path: '/',
        redirect: '/login'
      },
      {
        path: '/checkrecord',
        name: 'checkrecord',
        component: () => import('Pages/checkrecord.vue'),
        meta: {
          require: true,
          title: '检查记录'
        }
      },
      {
        path: '/report',
        name: 'report',
        component: () => import('Pages/report.vue'),
        meta: {
          require: true,
          title: '心电图报告'
        }
      },
      {
        path: '/opinion',
        name: 'opinion',
        component: () => import('Pages/opinion.vue'),
        meta: {
          require: true,
          title: '意见'
        }
      },
      {
        path: '/bind',
        name: 'bind',
        component: () => import('Pages/bind.vue'),
        meta: {
          require: true,
          title: '绑定'
        }
      },
      ...common,
      ...doctor,
      ...patient,
      ...test
    ]
    
    export default route

    把所有的路由文件挂载进去。

    src/service 目录

    接口配置文件目录,根据页面来定义文件

    同理我们只看src/service/api/index.js 和 src/service/api/login.js、src/pages/login/index.vue以及页面如何调用接口即可。

    src/service/api/login.js

    先定义好login接口

    const login = [
      {
        methodsName: 'loginByPhone',    // 方法名
        method: 'POST',
        desc: '登录',
        path: '/rest/app/login',      // 接口路径
        mockPath: '/rest/app/login',  
        params: {     // 参数配置  这里需要注意,只有配置的这些参数才能通过接口,所以需要传递的参数都要在这里配置
          phone: 1,
          password: 2,
          code: 3,
          codeid: '',
          clientid: ''
        }
      },
      {
        methodsName: 'login',
        method: 'POST',
        desc: '登录',
        path: '/rest/interfacesLoginController/login',
        mockPath: '/rest/interfacesLoginController/login',
        params: {
          username: 1,
          password: 2,
          code: 3,
          codeid: '',
          clientid: ''
        }
      },
      {
        methodsName: 'checkcode',
        method: 'POST',
        desc: '验证提取码',
        path: '/rest/app/medical/checksharecode',
        mockPath: '/rest/app/medical/checksharecode',
        params: {
          sharecode: '',
          id: ''
        }
      },
      {
        methodsName: 'getCode',
        method: 'POST',
        desc: '获取验证码',
        path: '/rest/interRandomCodeController/gererateRandomCode',
        mockPath: '',
        params: {
        }
      },
      {
        methodsName: 'getPublicKey',
        method: 'POST',
        desc: '获取公钥',
        path: '/rest/interRandomCodeController/clientIdAndPublicKey',
        mockPath: '',
        params: {
        }
      }
    ]
    
    export default login

    src/service/api/index.js

    挂载所有定义的接口文件

    import login from './login'
    import workcenter from './workcenter'
    import detail from './detail'
    import register from './register'
    import doctorpc from './doctorpc'
    import patientpc from './patientpc'
    import checklist from './checklist'
    export default {
      login,
      workcenter,
      detail,
      register,
      doctorpc,
      patientpc,
      checklist
    }

    src/pages/login/index.vue

    this.$api.login( params).then(data => {
        
    })
    // 这样调用登陆接口 this.$api.方法名(参数).then(res=>{})
    // 方法名定义不能重名

    其它目录

    这些目录还是包含很多东西,用户的信息保存,主体,工具函数这些,就不多说了。

    对于项目的维护还是需要看重,后期维护方便也便于管理。

  • 相关阅读:
    ElasticSearch Java API 增删改查操作
    ElasticSearch Java API连接报错NoNodeAvailableException解决方案
    Elasticsearch 使用Kibana实现基本的增删改查+mget批量查询+bulk批量操作
    Elasticsearch CentOS6.5下安装ElasticSearch6.2.4+elasticsearch-head+Kibana
    Elasticsearch 核心概念:NRT+索引+分片+副本
    Solr solrj4与solrj-7.7.2操作solrcloud
    Solr Centos6.5下搭建solr-7.7.2集群solrcloud+DIH操作
    分层图【p2939】[USACO09FEB]改造路Revamping Trails
    NOIP2018提高组模拟题(二)
    树链剖分【p3178】[HAOI2015]树上操作
  • 原文地址:https://www.cnblogs.com/shuangzikun/p/python_taotao_vue_pro.html
Copyright © 2011-2022 走看看