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

环境: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. decode encode

    https://blog.csdn.net/crylearner/article/details/38521685,python常用的十进制.16进制.字符串.字节串之间的转换

  2. HTML DOM 属性

    innerHTML 属性 获取元素内容的最简单方法是使用 innerHTML 属性. innerHTML 属性对于获取或替换 HTML 元素的内容很有用. 实例 下面的代码获取 id="in ...

  3. 并发,同步锁,Runnable,Callable,Future

    1.并发: 在我们的操作系统中,同一个时间点,有N个线程都想访问同一个程序!但是cpu只能运行一个! 这种情况就是! 多个线程在 同一个时间点 访问同一个资源,会引发线程不安全的问题! 怎么解决这种不 ...

  4. springcloud Ribbon学习笔记二

    之前介绍了如何搭建eureka服务并开发了一个用户服务成功注册到了eureka中,接下来介绍如何通过ribbon来从eureka中获取用户服务: springcloud ribbon提供客户端的负载均 ...

  5. L333 Should You Listen to Music While You Work?

    Should You Listen to Music While You Work? "Whistle while you work" is classic advice, str ...

  6. Hadoop学习笔记01_Hadoop搭建

    想往大数据方向转, 难度肯定是有的. 基础知识肯定是要有的,如果是熟悉JAVA开发的人,转向应该优势大. 像我这样的,只有Linux基础以及简单的PHP基础的人,转向难度很大.但是事在人为,努力学习多 ...

  7. python自学第9天,装饰器

    装饰器:本质是函数(装饰其它函数) 就是为其它函数添加附加功能 原则:1.不能修改被装饰函数的源代码 2.不能修改被装饰的函数的调用方式 实现装饰器知识储备: 1.函数即变量 2.高阶函数:a.把一个 ...

  8. IDE Fix Pack 6.4.2 released (bugfix release)

    IDE Fix Pack 6.4.2 addresses two bugs. It fixes an issue with the TCustomListBox.ResetContent patch ...

  9. Codeforces1076F. Summer Practice Report(贪心+动态规划)

    题目链接:传送门 题目: F. Summer Practice Report time limit per test seconds memory limit per test megabytes i ...

  10. hive数据导出到本地目录 抛异常

    经过反复试验,最终重启hdfs和hive解决问题 hive> insert overwrite local directory '/Users/wooluwalker/Desktop/' sel ...