第十一章 前端開(kāi)發(fā)環(huán)境搭建

一啄巧、前端開(kāi)發(fā)環(huán)境
  1. Visual Studio Code
  2. Node JS
  3. Webpack:npm install webpack -g
  4. vue-cli:npm install vue-cli -g
  5. 淘寶鏡像:npm install -g cnpm --registry=https://registry.npm.taobao.org
  6. Yarn:npm i yarn -g -verbose
二、創(chuàng)建項(xiàng)目
vue init webpack icupo-web
npm install
三、安裝Element UI
npm install element-ui

按照官網(wǎng)的引入方式在main.js中引入:

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
...
Vue.use(ElementUI)
四铁坎、安裝scss
# 注意安裝的版本
npm install node-sass@4.14.1
npm install sass-loader@7.3.1

配置build/webpack.base.conf.js鉴逞,注意這個(gè)不要添加

      {
        test: /\.scss$/,
        loaders: ['style', 'css', 'sass']
      }
五扰法、安裝axios與js-cookie

對(duì)axios的封裝有幾個(gè)好處:

  • 統(tǒng)一Url配置
  • 統(tǒng)一Api請(qǐng)求
  • request攔截器,加入請(qǐng)求頭
  • response攔截器决左,統(tǒng)一錯(cuò)誤處理,頁(yè)面重定向
  • 結(jié)合vuex做全局的loading動(dòng)畫逗载,或錯(cuò)誤處理
  • 將axios封裝成vue插件

5.1 安裝axios和js-cookie

npm install axios
npm install js-cookie

5.2 定義全局常量文件src/utils/global.js哆窿,并掛載到Vue,通過(guò)this.global調(diào)用常量的值厉斟。

// 后臺(tái)管理系統(tǒng)服務(wù)器地址
export const baseUrl = 'http://localhost:8001'

// 系統(tǒng)數(shù)據(jù)備份還原服務(wù)器地址
export const backupBaseUrl = 'http://localhost:8002'

export default {
  baseUrl,
  backupBaseUrl
}

在main.js中掛載

import global from '@/utils/global'
...
Vue.prototype.global = global

5.3 配置axios挚躯,src/http/config.js,一些默認(rèn)的配置項(xiàng)

import { baseUrl } from '@/utils/global'

export default {
    method: 'get',
    baseUrl: baseUrl,
    Headers: {
        'Content-Type': 'application/json;charset=UTF-8'
    },
    data: {},
    timeout: 300000,
    withCredentials: true,
    responseType: 'json'
}

5.4 axios的請(qǐng)求與響應(yīng)的處理擦秽,src/http/axios.js

  • 導(dǎo)入配置文件的信息到axios對(duì)象
  • 發(fā)送請(qǐng)求的時(shí)候攜帶token码荔,如果token不存在,則重定向到登錄頁(yè)面
  • 統(tǒng)一處理響應(yīng)
import axios from 'axios'
import config from './config'
import Cookies from 'js-cookie'
import router from '../router'

export default function $axios(options) {
    return new Promise((resolve, reject) => {
        const instance = axios.create({
            baseURL: config.baseUrl,
            headers: config.headers,
            timeout: config.timeout,
            withCredentials: config.withCredentials
        })
        // request 請(qǐng)求攔截器
        instance.interceptors.request.use(
            config => {
                let token = Cookies.get('token')
                if (token) {
                    config.headers.Authorization = 'Bearer ' + token
                } else {
                    router.push('/login')
                }
                return config
            },
            error => {
                return Promise.reject(error)
            }
        )
        // response 響應(yīng)攔截器
        instance.interceptors.response.use(
            response => {
                return response.data
            },
            err => {
                if (err && err.response) {
                    switch (err.response.status) {
                        case 400:
                            err.message = '請(qǐng)求錯(cuò)誤'
                            break
                        case 401:
                            err.message = '未授權(quán)感挥,請(qǐng)登錄'
                            break
                        case 403:
                            err.message = '拒絕訪問(wèn)'
                            break
                        case 404:
                            err.message = `請(qǐng)求地址出錯(cuò): ${err.response.config.url}`
                            break
                        case 408:
                            err.message = '請(qǐng)求超時(shí)'
                            break
                        case 500:
                            err.message = '服務(wù)器內(nèi)部錯(cuò)誤'
                            break
                        case 501:
                            err.message = '服務(wù)未實(shí)現(xiàn)'
                            break
                        case 502:
                            err.message = '網(wǎng)關(guān)錯(cuò)誤'
                            break
                        case 503:
                            err.message = '服務(wù)不可用'
                            break
                        case 504:
                            err.message = '網(wǎng)關(guān)超時(shí)'
                            break
                        case 505:
                            err.message = 'HTTP版本不受支持'
                            break
                        default:
                    }
                }
                console.error(err)
                return Promise.reject(err)
            }
        )
        // 請(qǐng)求處理
        instance(options).then(res => {
            resolve(res)
            return false
        }).catch(error => {
            reject(error)
        })
    })
}

5.5 掛載api缩搅,可以通過(guò) "this.$api.模塊.方法" 的方式調(diào)用API。

  • src/http/index.js
// 導(dǎo)入所有接口
import api from './api'

const install = Vue => {
  if (install.installed) {
    return
  }

  install.installed = true

  Object.defineProperties(Vue.prototype, {
    $api: {
      get () {
        return api
      }
    }
  })
}

export default install

  • src/http/api.js
/* 
 * 接口統(tǒng)一集成模塊
 */
import * as login from './modules/login'
import * as user from './modules/user'
import * as dept from './modules/dept'
import * as role from './modules/role'
import * as menu from './modules/menu'
import * as dict from './modules/dict'
import * as config from './modules/config'
import * as log from './modules/log'
import * as loginlog from './modules/loginlog'


// 默認(rèn)全部導(dǎo)出
export default {
    login,
    user,
    dept,
    role,
    menu,
    dict,
    config,
    log,
    loginlog
}
  • main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import global from './utils/global'
import api from './http'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.config.productionTip = false

Vue.use(api)
Vue.use(ElementUI)

Vue.prototype.global = global

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

5.6 一些api的案例

import axios from '../axios'

/* 
 * 系統(tǒng)配置模塊
 */

// 保存
export const save = (data) => {
    return axios({
        url: '/config/save',
        method: 'post',
        data
    })
}
// 刪除
export const batchDelete = (data) => {
    return axios({
        url: '/config/delete',
        method: 'post',
        data
    })
}
// 分頁(yè)查詢
export const findPage = (data) => {
    return axios({
        url: '/config/findPage',
        method: 'post',
        data
    })
}
import axios from '../axios'

/* 
 * 機(jī)構(gòu)管理模塊
 */

// 保存
export const save = (data) => {
    return axios({
        url: '/dept/save',
        method: 'post',
        data
    })
}
// 刪除
export const batchDelete = (data) => {
    return axios({
        url: '/dept/delete',
        method: 'post',
        data
    })
}
// 查詢機(jī)構(gòu)樹(shù)
export const findDeptTree = () => {
    return axios({
        url: '/dept/findTree',
        method: 'get'
    })
}
import axios from '../axios'

/* 
 * 字典管理模塊
 */

// 保存
export const save = (data) => {
    return axios({
        url: '/dict/save',
        method: 'post',
        data
    })
}
// 刪除
export const batchDelete = (data) => {
    return axios({
        url: '/dict/delete',
        method: 'post',
        data
    })
}
// 分頁(yè)查詢
export const findPage = (data) => {
    return axios({
        url: '/dict/findPage',
        method: 'post',
        data
    })
}
import axios from '../axios'

/* 
 * 操作日志模塊
 */

// 刪除
export const batchDelete = (data) => {
    return axios({
        url: '/log/delete',
        method: 'post',
        data
    })
}
// 分頁(yè)查詢
export const findPage = (data) => {
    return axios({
        url: '/log/findPage',
        method: 'post',
        data
    })
}
import axios from '../axios'

/* 
 * 系統(tǒng)登錄模塊
 */

// 登錄
export const login = data => {
    return axios({
        url: 'login',
        method: 'post',
        data
    })
}

// 登出
export const logout = () => {
    return axios({
        url: 'logout',
        method: 'get'
    })
}
import axios from '../axios'

/* 
 * 操作日志模塊
 */

// 刪除
export const batchDelete = (data) => {
    return axios({
        url: '/loginlog/delete',
        method: 'post',
        data
    })
}
// 分頁(yè)查詢
export const findPage = (data) => {
    return axios({
        url: '/loginlog/findPage',
        method: 'post',
        data
    })
}
import axios from '../axios'

/* 
 * 菜單管理模塊
 */

 // 保存
export const save = (data) => {
    return axios({
        url: '/menu/save',
        method: 'post',
        data
    })
}
// 刪除
export const batchDelete = (data) => {
    return axios({
        url: '/menu/delete',
        method: 'post',
        data
    })
}
// 查找導(dǎo)航菜單樹(shù)
export const findNavTree = (params) => {
    return axios({
        url: '/menu/findNavTree',
        method: 'get',
        params
    })
}
// 查找導(dǎo)航菜單樹(shù)
export const findMenuTree = () => {
    return axios({
        url: '/menu/findMenuTree',
        method: 'get'
    })
}
import axios from '../axios'

/* 
 * 角色管理模塊
 */

// 保存
export const save = (data) => {
    return axios({
        url: '/role/save',
        method: 'post',
        data
    })
}
// 刪除
export const batchDelete = (data) => {
    return axios({
        url: '/role/delete',
        method: 'post',
        data
    })
}
// 分頁(yè)查詢
export const findPage = (data) => {
    return axios({
        url: '/role/findPage',
        method: 'post',
        data
    })
}
// 查詢?nèi)?export const findAll = () => {
    return axios({
        url: '/role/findAll',
        method: 'get'
    })
}
// 查詢角色菜單集合
export const findRoleMenus = (params) => {
    return axios({
        url: '/role/findRoleMenus',
        method: 'get',
        params
    })
}
// 保存角色菜單集合
export const saveRoleMenus = (data) => {
    return axios({
        url: '/role/saveRoleMenus',
        method: 'post',
        data
    })
}
import axios from '../axios'

/* 
 * 用戶管理模塊
 */

// 保存
export const save = (data) => {
    return axios({
        url: '/user/save',
        method: 'post',
        data
    })
}
// 刪除
export const batchDelete = (data) => {
    return axios({
        url: '/user/delete',
        method: 'post',
        data
    })
}
// 分頁(yè)查詢
export const findPage = (data) => {
    return axios({
        url: '/user/findPage',
        method: 'post',
        data
    })
}
// 導(dǎo)出Excel用戶信息
export const exportUserExcelFile = (data) => {
    return axios({
        url: '/user/exportUserExcelFile',
        method: 'post',
        data
    })
}
// 查找用戶的菜單權(quán)限標(biāo)識(shí)集合
export const findPermissions = (params) => {
    return axios({
        url: '/user/findPermissions',
        method: 'get',
        params
    })
}
// 根據(jù)用戶名查找
export const findByName = (params) => {
    return axios({
        url: '/user/findByName',
        method: 'get',
        params
    })
}
// 更新用戶密碼
export const updatePassword = (params) => {
    return axios({
        url: '/user/updatePassword',
        method: 'get',
        params
    })
}

3.4 登錄邏輯

login() {
  this.$api.login.login().then(function(res) {
    Cookies.set('token', res.token)
    router.push('/')
  }).catch(function(res) {
    // 其它處理
  })
}
四触幼、國(guó)際化
  1. 安裝依賴
npm install vue-i18n@8.26.5
  1. 配置src/i18n/index.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

// 注冊(cè)i18n實(shí)例并引入語(yǔ)言文件硼瓣,文件格式等下解析
const i18n = new VueI18n({
  locale: 'zh_cn',
  messages: {
    'zh_cn': require('@/assets/languages/zh_cn.json'),
    'en_us': require('@/assets/languages/en_us.json')
  }
})

export default i18n

{
    "common": {
        "home": "首頁(yè)",
        "login": "登錄",
        "logout": "退出登錄",
        "doc": "文檔",
        "blog": "博客",
        "projectRepo": "項(xiàng)目",
        "myMsg": "我的消息",
        "config": "系統(tǒng)配置",           
        "backup": "備份",
        "restore": "還原",
        "backupRestore": "備份還原",
        "versionName": "版本名稱",             
        "exit": "退出"
    },
    "action": {
        "operation": "操作",
        "add": "新增",
        "edit": "編輯",
        "delete": "刪除",
        "batchDelete": "批量刪除",
        "search": "查詢",
        "loading": "拼命加載中",
        "submit": "提交",
        "comfirm": "確定",
        "cancel": "取消",
        "reset": "重置"
        
    }
}
{
    "common": {
        "home": "Home",
        "login": "Login",
        "logout": "Logout",
        "doc": "Document",
        "blog": "Blog",
        "projectRepo": "Project",
        "myMsg": "My Message",
        "config": "Config",
        "backup": "Backup",  
        "restore": "Restore",  
        "backupRestore": "Backup Restore",  
        "versionName": "Version",  
        "exit": "Exit"
    },
    "action": {
        "operation": "Operation",
        "add": "Add",
        "edit": "Edit",
        "delete": "Delete",
        "batchDelete": "Batch Delete",
        "search": "Search",
        "loading": "loading",
        "submit": "Submit",
        "comfirm": "Comfirm",
        "cancel": "Cancel",
        "reset": "Reset"
    }
}
import Vue from 'vue'
import App from './App'
import router from './router'
import i18n from './i18n'
import global from './utils/global'
import api from './http'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.config.productionTip = false

Vue.use(api)
Vue.use(ElementUI)

Vue.prototype.global = global

/* eslint-disable no-new */
new Vue({
  el: '#app',
  i18n,
  router,
  components: { App },
  template: '<App/>'
})

  1. 切換語(yǔ)言函數(shù):
changeLanguage(lang) {
  lang === '' ? 'zh_cn' : lang
  this.$i18n.locale = lang
}
  1. 使用方法:
# html中使用
{{$t('common.doc')}}
# js中使用
i18n.t('message.timeout')
五、全局狀態(tài)
  1. 安裝
npm install vuex@3.6.2
  1. 編寫配置文件置谦,src/store/index.js
import Vue from 'vue'
import vuex from 'vuex'

Vue.use(vuex);

// 引入子模塊
import app from './modules/app'
import tab from './modules/tab'
import user from './modules/user'
import menu from './modules/menu'

const store = new vuex.Store({
    modules: {
        app: app,
        tab: tab,
        user: user,
        menu: menu
    }
})

export default store
  1. app.js堂鲤。是屬于應(yīng)用內(nèi)的全局性的配置,比如主題色媒峡、導(dǎo)航欄收縮狀態(tài)等瘟栖,詳見(jiàn)注釋。
export default {
  state: {
    test: false, // 測(cè)試
    menuRouteLoaded: false // 菜單和路由是否已經(jīng)加載
  },
  getters: {
    test (state) {
      return state.test
    }
  },
  mutations: {
    setTest (state, payload) {
      state.test = payload.test
    },
    menuRouteLoaded (state, menuRouteLoaded) { // 改變菜單和路由的加載狀態(tài)
      state.menuRouteLoaded = menuRouteLoaded
    }
  },
  actions: {
  }
}

import Vue from 'vue'
import App from './App'
import router from './router'
import i18n from './i18n'
import store from './store'
import global from './utils/global'
import api from './http'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.config.productionTip = false

Vue.use(api)
Vue.use(ElementUI)

Vue.prototype.global = global

/* eslint-disable no-new */
new Vue({
  el: '#app',
  i18n,
  router,
  store,
  components: { App },
  template: '<App/>'
})

  1. 通過(guò)computed計(jì)算屬性引入store屬性谅阿,
computed: {
  ...mapState({
    ***: state => state.app.***
  })
}
  1. 通過(guò)語(yǔ)句this.$store.commit('mothodName', {})來(lái)修改值
六半哟、全站配置
  1. 樣式文件src/assets/css/site.css
# 內(nèi)容
* {
    margin: 0;
    padding: 0;
    user-select: none;
}

# main.js中引入
import './assets/css/site.css'
  1. 全局圖片文件src/assets/img/site。
  2. App.vue
<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
</style>
  1. vscode調(diào)整tab為2個(gè)空格
# .eslintrc.js -> rules中添加
"indent": ["error", "tab"]
七签餐、自定義圖標(biāo)功能

打開(kāi)阿里icon寓涨,注冊(cè) >登錄>圖標(biāo)管理>我的項(xiàng)目。項(xiàng)目名稱:el-icon-third氯檐。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戒良,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子男摧,更是在濱河造成了極大的恐慌蔬墩,老刑警劉巖译打,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拇颅,居然都是意外死亡奏司,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門樟插,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)韵洋,“玉大人,你說(shuō)我怎么就攤上這事黄锤√掠В” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵鸵熟,是天一觀的道長(zhǎng)副编。 經(jīng)常有香客問(wèn)我,道長(zhǎng)流强,這世上最難降的妖魔是什么痹届? 我笑而不...
    開(kāi)封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮打月,結(jié)果婚禮上队腐,老公的妹妹穿的比我還像新娘。我一直安慰自己奏篙,他們只是感情好柴淘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著秘通,像睡著了一般为严。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上充易,一...
    開(kāi)封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天梗脾,我揣著相機(jī)與錄音荸型,去河邊找鬼盹靴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瑞妇,可吹牛的內(nèi)容都是我干的稿静。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼辕狰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼改备!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蔓倍,我...
    開(kāi)封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤悬钳,失蹤者是張志新(化名)和其女友劉穎盐捷,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體默勾,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碉渡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了母剥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滞诺。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖环疼,靈堂內(nèi)的尸體忽然破棺而出习霹,到底是詐尸還是另有隱情,我是刑警寧澤炫隶,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布淋叶,位于F島的核電站,受9級(jí)特大地震影響伪阶,放射性物質(zhì)發(fā)生泄漏爸吮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一望门、第九天 我趴在偏房一處隱蔽的房頂上張望形娇。 院中可真熱鬧,春花似錦筹误、人聲如沸桐早。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)哄酝。三九已至,卻和暖如春祷膳,著一層夾襖步出監(jiān)牢的瞬間陶衅,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工直晨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留搀军,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓勇皇,卻偏偏與公主長(zhǎng)得像罩句,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子敛摘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容