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

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

其它目录

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

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

Vue+webpack项目配置便于维护的目录结构的更多相关文章

  1. Vue + webpack 项目配置化、接口请求统一管理

    准备工作 需求由来: 当项目越来越大的时候提高项目运行编译速度.压缩代码体积.项目维护.bug修复......等等成为不得不考虑而且不得不做的问题.  又或者后面其他同事接手你的模块,或者改你的bug ...

  2. 从零开始:一个正式的vue+webpack项目的目录结构是怎么形成的

    如何从零开始一个vue+webpack前端工程工作流的搭建,首先我们先从项目的目录结构入手.一个持续可发展,不断加入新功能,方便后期维护的目录结构究竟是长什么样子的?接下来闰土大叔带你们一起手摸手学起 ...

  3. VUE前端项目配置代理解决跨域问题

    VUE前端项目配置代理解决跨域问题 问题如下,经常在本地调试接口出现这种问题 解决方式1:Chrome 的扩展插件 以前使用Chrome 的扩展插件,但是有时候还是会出现莫名其妙的问题. 需要梯子才行 ...

  4. 第三百九十四节,Django+Xadmin打造上线标准的在线教育平台—Xadmin后台进阶开发配置2,以及目录结构说明

    第三百九十四节,Django+Xadmin打造上线标准的在线教育平台—Xadmin后台进阶开发配置2,以及目录结构说明 设置后台列表页面可以直接修改字段内容 在当前APP里的adminx.py文件里的 ...

  5. Vue+webpack项目的多环境打包配置

    背景:由于需要将应用部署到线上开发环境.线上测试环境.线上预发环境.线上生产环境,而每个环境的访问地址是不同的.如果每次更改请求地址未免有些繁琐,就考虑在本地进行一次性配置. 代码管理工具:git 代 ...

  6. vue项目搭建及创建、目录结构、项目启动、全局配置

    Vue项目环境搭建 """ node ~~ python:node是用c++编写用来运行js代码的 npm(cnpm) ~~ pip:npm是一个终端应用商城,可以换国内 ...

  7. 在找一份相对完整的Webpack项目配置指南么?这里有

    Webpack已经出来很久了,相关的文章也有很多,然而比较完整的例子却不是很多,让很多新手不知如何下脚,下脚了又遍地坑 说实话,官方文档是蛮乱的,而且有些还是错的错的..很多配置问题只有爬过坑才知道 ...

  8. Vue + webpack 项目实践

    Vue.js 是一款极简的 mvvm 框架,如果让我用一个词来形容它,就是 “轻·巧” .如果用一句话来描述它,它能够集众多优秀逐流的前端框架之大成,但同时保持简单易用.废话不多说,来看几个例子: & ...

  9. nodejs, vue, webpack 项目实践

    vue 及 webpack,均不需要与nodejs一期使用,他们都可以单独使用到任何语言的框架中. http://jiongks.name/blog/just-vue/ https://cn.vuej ...

随机推荐

  1. linq查询数值为null的问题以及数据表的关联计算问题

    说明:下面实例都是我进行项目开发时的真实部分代码,毫无保留 一.数据表的关联计算 //把当前年度的分差计算出来,建立两个关联的数据表 try { using(TransactionScope scop ...

  2. CodeForces - 1015 D.Walking Between Houses

    Description Natasha is planning an expedition to Mars for nn people. One of the important tasks is t ...

  3. constructor&object 的对比

    Constructor vs object A constructor is a special member function in the class to allocate memory to ...

  4. copy GC 和 mark & compaction GC的算法异同

    先标记 然后 copy GC是,对所有child,判断, 如果child没有被访问过,那么拷贝到新地址,child的forwording指向新地址,child标记为已访问,把自己对child的引用改为 ...

  5. golang结构体、接口、反射

    struct结构体 struct用来自定义复杂数据结构,可以包含多个字段属性,可以嵌套; go中的struct类型理解为类,可以定义方法,和函数定义有些许区别; struct类型是值类型. struc ...

  6. 磁性窗体设计C#(二)

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...

  7. 使用CSMA/CD协议一个计算题

    题干: 首先计算一下A这个以太网所容许的最短的帧它的发送帧的长度时间为: (8(前同步码为8)+64(最短帧长))*8(单位转换b到B)=576比特 有关于单位转换: B是Byte的缩写,B就是Byt ...

  8. java JDBC编程流程步骤

    JDBC:Java Data Base Connection JDBC是用于运行sql语句并从数据库中获取新新的java API. JDBC是用来(让我们的程序)通过网络来操作数据库的,作用非常重要: ...

  9. Python 实现累加计数的几种方法

    #要实现累加,关键在于数据存在哪儿,怎么使每次累加的都是同一个变量 行为像静态变量 #前两种都是数据存到类的成员变量, # 类利用__call__ class foo: def __init__(se ...

  10. Oracle数据库死锁和MySQL死锁构造和比较

    最近在复习数据库的事务隔离性,顺便构造了一下在Oracle上和MySQL上的死锁以比较异同. 在Oracle上面的实验 在Oracle中,因为是显式提交,所以默认可以认为在一个会话中若没有使用comm ...