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=>{})
// 方法名定義不能重名

其它目錄

這些目錄還是包含很多東西,用戶的信息保存,主體,工具函數這些,就不多說了。

對於項目的維護還是需要看重,后期維護方便也便於管理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM