【前端vue進階實戰】:從零打造一個流程圖、拓撲圖項目【Nuxt.js + Element + Vuex】 (三)

前面二,下面我們實現右鍵菜單、http通信、路由。

本系列教程是用Vue.js + Nuxt.js + Element + Vuex + 開源js繪圖庫,打造一個屬於自己的在線繪圖軟件,最終效果:topology.le5le.com 。如果你覺得好,歡迎給文章和開源庫點贊,讓我們更有動力去做好!

本系列教程源碼地址:Github

目錄

右鍵菜單

右鍵菜單原理很簡單:自定義html的oncontextmenu事件:

<div id="topology-canvas" class="full" @contextmenu="onContextMenu($event)"></div>
複製代碼

屏蔽默認右鍵菜單事件,計算右鍵鼠標位置,彈出一個我們自己的div自定義菜單即可

onContextMenu(event) {
      event.preventDefault()
      event.stopPropagation()

      if (event.clientY + 360 < document.body.clientHeight) {
        this.contextmenu = {
          left: event.clientX + 'px',
          top: event.clientY + 'px'
        }
      } else {
        this.contextmenu = {
          left: event.clientX + 'px',
          bottom: document.body.clientHeight - event.clientY + 'px'
        }
      }
    }
複製代碼
<div class="context-menu" v-if="contextmenu.left" :style="this.contextmenu">
     <ul>
		   <li>菜單一</li>
			 <li>菜單二</li>
			 <li>菜單三</li>
		 </ul>
</div>
複製代碼

在本項目中,封裝了一個右鍵菜單組件“CanvasContextMenu”,通過父組件,傳遞canvas實例和選中的屬性數據

 props: {
    canvas: {
      type: Object,
      require: true
    },
    props: {
      type: Object,
      require: true
    }
  }
複製代碼

其中,屬性props含義爲:

props: {
  node: null,       // 選中節點
  line: null,         // 選中連線
  nodes: null,    // 選中多個節點
  multi: false,   // 選中多個節點/連線
  locked: false // 選中對象是否被鎖定
}
複製代碼

然後,我們根據菜單事件和屬性props來調用canvas的相應接口函數,參考開發文檔

http通信

這裏,我們不去從零寫個後端服務,直接採用topology.le5le.com線上接口服務。

代理配置

首先,我們需要給nuxt.config.js添加http代理配置,這樣開發環境下的http請求,自動代理轉發給topology.le5le.com,獲取到真實數據。

 axios: {
    proxy: true
  },
  proxy: {
    '/api/': 'http://topology.le5le.com/',
    '/image/': 'http://topology.le5le.com/'
  },
複製代碼

其中,proxy的含義是指:所有/api/、/image/開頭的請求,自動轉發給http://topology.le5le.com/ ,其他的不轉發。 通常,我們通過前綴/api/表示這是後端接口請求,而不是靜態資源請求;/image/表示靜態資源圖片請求。

用戶登錄

1. 右上角添加用戶頭像和暱稱

由於這裏,暫時沒有多頁面共享用戶信息,因此,我們直接在layouts/default的頂部導航欄和data裏添加用戶數據

data() {
    return {
      about: false,
      license: false,
      joinin: false,
      lineNames: ['curve', 'polyline', 'line'],
      arrowTypes: [
        '',
        'triangleSolid',
        'triangle',
        'diamondSolid',
        'diamond',
        'circleSolid',
        'circle',
        'line',
        'lineUp',
        'lineDown'
      ],
      user: null
    }
}
	
	
	
	<el-submenu index="user" v-if="user">
			<template slot="title">
				<el-avatar
					src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
					:size="24"
				></el-avatar>
				{{user.username}}
			</template>
			<el-menu-item @click="onSignOut">退出</el-menu-item>
		</el-submenu>
		<el-menu-item v-if="!user">
			<a @click="onLogin">註冊 / 登錄</a>
		</el-menu-item>
複製代碼

大型項目,可能需要使用vuex去保存用戶數據

2. 實現登錄

這裏,我們直接省略登錄頁面,直接跳轉到線上登錄頁面account.le5le.com,共享登錄狀態。

凡是le5le.com的子域名,通過共享cookie中的token來共享le5le.com的登錄狀態。首先,我們修改本地電腦的host文件,新增一條local.le5le.com子域名,映射到本地電腦:

127.0.0.1 local.le5le.com
複製代碼

如何修改host文件,請google。

然後,我們把 http://localhost:3000/ 換成 local.le5le.com:3000/ 去在瀏覽器中打開我們的開發頁面,這時,我們就可以點擊右上角“登錄/註冊”,去登錄。

2.1 第一次打開網頁,讀取用戶是否登錄

在le5le.com上,是使用jwt的方式去用戶認證的。jwt的token值存儲在cookie中,方便子域名共享登錄。然後每個http請求headers裏面加上Authorization: token值,後端服務就可以認證用戶身份。

在第一次打開網頁初始化時,只需在請求後端服務/api/user/profile獲取用戶即可。當接口/api/user/profile返回用戶數據,表示用戶已登錄;當返回401表示未登錄。這裏,我們先判斷了是否存在cookie下的token在請求用戶接口。參考default.vue:

async getUser() {
      if (this.$cookies.get('token')) {
        this.user = await this.$axios.$get('/api/user/profile')
      }
},
複製代碼

async和await是成對出現的。函數名前面加async,函數裏面就可以使用await表示必須等待await後面的函數執行完得到返回結果,才繼續執行後面的代碼。通常用於等待異步請求結果,避免回調。即使沒有異步也是可以的。

爲了每次請求自動把cookie裏面的token添加到headers裏面,我們需要按照下面操作,寫一個http請求的攔截器。

2.2 http攔截器

http攔截器的作用時,每次請求和數據返回時,自動幫我們處理一些全局公用操作。比如這裏的身份認證token添加。

2.2.1 新建一個plugins/axios.js插件文件 通過axios插件的方式實現http攔截器。

export default function({ $axios, app, redirect }) {
  // 設置token,添加Authorization到headers
  $axios.setToken(app.$cookies.get('token'))

  $axios.onResponse(response => {
	  // 統一錯誤處理。如果發現error字段,則發送一個notice/error錯誤消息。在default.vue監聽並彈出右上角錯誤提示框
    if (response.error) {
      app.store.commit('notice/error', { text: response.error })
    }
  })

  $axios.onError(error => {
	  // 通過code狀態碼判斷接口是否出錯。請自行google學習http狀態碼
    const code = parseInt(error.response && error.response.status)
    if (code === 401) {
		  // 需要登錄
      app.store.commit('notice/error', {
        text: '請先登錄!'
      })
    } else if (code === 403) {
		  // 無權限訪問
      redirect('/')
    } else if (code === 500) {
      app.store.commit('notice/error', {
        text: error.response.error || '服務錯誤,請稍後重試!'
      })
    }
  })
}
複製代碼

2.2.2 nuxt.config.js中配置加載axios插件

plugins: ['@/plugins/element-ui', '@/plugins/axios'],
複製代碼

2.2.3 添加cookie插件 因爲我們需要讀取cookie,所有安裝一個cookie插件,方便使用。

yarn add cookie-universal-nuxt --save
複製代碼

nuxt.config.js中

modules: [
   // Doc: https://axios.nuxtjs.org/usage
   '@nuxtjs/axios',
   'cookie-universal-nuxt'
 ],
複製代碼

然後,可以直接在我們的plugins/axios.js和(default).vue中使用了。

路由,添加一個首頁列表頁面

nuxt.js不需要去寫router.js,約定pages下的vue文件就是路由。比較省事。 我們添加一個新的index.vue首頁,把原index.vue改成workspace.vue。 新index.vue:

<template>
 <div class="page-list">
   <div>
     <div class="nav">
       <label>熱門圖文</label>
     </div>
     <div class="flex wrap">
       <div
         class="topo"
         v-for="(item, index) of data.list"
         :key="index"
         :title="item.desc"
         @click="onOpen(item)"
       >
         <div class="image">
           <img :src="item.image" />
         </div>
         <div class="ph15 pv10">
           <div class="title line one" :title="item.name">{{ item.name }}</div>
           <div class="desc line two mt5" :title="item.desc">{{ item.desc }}</div>
           <div class="flex mt5">
             <div class="full flex middle">
               <el-avatar
                 src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
                 :size="24"
               ></el-avatar>
               <span class="ml5">{{ item.username }}</span>
             </div>
             <div>
               <span class="hover pointer mr15" title="贊">
                 <i
                   class="iconfont"
                   :class="{'iconfont icon-appreciate':!item.stared, 'iconfont icon-appreciatefill':item.stared}"
                 ></i>
                 <span class="ml5">{{ item.star || 0 }}</span>
               </span>
               <span class="hover pointer" title="收藏">
                 <i
                   class="iconfont"
                   :class="{'iconfont icon-like':!item.favorited, 'iconfont icon-likefill':item.favorited}"
                 ></i>
                 <span class="ml5">{{ item.hot || 0 }}</span>
               </span>
             </div>
           </div>
         </div>
       </div>
     </div>
     <div>
       <el-pagination
         @current-change="getList"
         :current-page="search.pageIndex"
         :page-size="8"
         layout=" prev, pager, next, jumper, total"
         :total="data.count"
       ></el-pagination>
     </div>
   </div>
 </div>
</template>

<script>
export default {
 data() {
   return {
     data: {
       list: [],
       count: 0
     },
     search: {
       pageIndex: 1,
       pageCount: 8
     }
   }
 },
 created() {
   this.getList()
 },
 methods: {
   async getList() {
     this.data = await this.$axios.$get(
       `/api/topologies?pageIndex=${this.search.pageIndex}&pageCount=${this.search.pageCount}`
     )
   },
   onOpen(item) {
     this.$router.push({ path: '/workspace', query: { id: item.id } })
   }
 }
}
</script>
複製代碼

index.vue 主要包含:1. getList獲取首頁列表數據;2. this.$router.push({ path: '/workspace', query: { id: item.id } }) 打開具體頁面

路由詳情:zh.nuxtjs.org/guide/routi…

最後

自此,一個麻雀雖小五臟俱全的小項目就完成了,包含:框架搭建、插件、vuex、身份認證、http通信、路由等功能。

整個項目功能細節還不完善,歡迎大家提pr:

完整細節可參考:topology.le5le.com/開發文檔 。可加入貢獻者名單哦!也歡迎加羣交流討論:

topology技術討論羣2

 

 

如何貢獻

通過GitHub的pr方式: 0. 閱讀開發文檔,瞭解相關屬性。

  1. fork倉庫到自己名下
  2. 本地修改並提交到自己的git倉庫
  3. 在自己的fork倉庫找到 “Pull request” 按鈕,提交file

開源項目不易,歡迎大家一起參與,給【文章GitHub開源庫】點星點贊,或資助服務器:

file

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章