[Advanced front-end vue combat]: Create a flowchart and topology map project from scratch [Nuxt.js + Element + Vuex] (3)

Continuing from the previous two , let's implement the right-click menu, http communication, and routing.

This series of tutorials uses Vue.js + Nuxt.js + Element + Vuex + open source js drawing library to create your own online drawing software, the final effect: http://topology.le5le.com . If you think it is good, welcome to like the article and open source library , let us be more motivated to do well!

The source code address of this series of tutorials: Github

content

right-click menu

The principle of the right-click menu is very simple: customize the oncontextmenu event of html:

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

Block the default right-click menu event, calculate the right-click mouse position, and pop up a custom div menu of our own

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>

In this project, a right-click menu component "CanvasContextMenu" is encapsulated, and the canvas instance and selected attribute data are passed through the parent component.

 props: {
    canvas: {
      type: Object,
      require: true
    },
    props: {
      type: Object,
      require: true
    }
  }

Among them, the meaning of attribute props is:

props: {
  node: null,       // 选中节点
  line: null,         // 选中连线
  nodes: null,    // 选中多个节点
  multi: false,   // 选中多个节点/连线
  locked: false // 选中对象是否被锁定
}

Then, we call the corresponding interface function of canvas according to menu events and property props, refer to the development documentation

http communication

Here, instead of writing a backend service from scratch, we directly use the topology.le5le.com online interface service.

proxy configuration

First, we need to add http proxy configuration to nuxt.config.js, so that http requests in the development environment are automatically forwarded to topology.le5le.com by the proxy to obtain real data.

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

Among them, the meaning of proxy means: all requests starting with /api/ and /image/ are automatically forwarded to http://topology.le5le.com/ , and others are not forwarded. Usually, we use the prefix /api/ to indicate that this is a back-end interface request, not a static resource request; /image/ indicates a static resource image request.

User login

1. Add user avatar and nickname in the upper right corner

Since there is no multi-page sharing of user information for the time being, we directly add user data to the top navigation bar and data of layouts/default

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>

Large projects may need to use vuex to save user data

2. Implement login

Here, we simply omit the login page and jump directly to the online login page account.le5le.com to share the login status.

All subdomains of le5le.com share the login status of le5le.com by sharing the token in the cookie. First, we modify the host file of the local computer, add a subdomain name local.le5le.com, and map it to the local computer:

127.0.0.1 local.le5le.com

How to modify the host file, please google.

Then, we replace http ://localhost:3000/ with http://local.le5le.com:3000/ to open our development page in the browser. At this time, we can click "Login/Register" in the upper right corner " to log in.

2.1 Open the web page for the first time and read whether the user is logged in

On le5le.com, user authentication is done using jwt . The token value of jwt is stored in a cookie, which is convenient for subdomains to share login. Then add the Authorization: token value to each http request header, and the back-end service can authenticate the user's identity.

When opening the web page initialization for the first time, you only need to request the backend service /api/user/profile to get the user. When the interface /api/user/profile returns user data, it means that the user is logged in; when it returns 401, it means not logged in. Here, we first judge whether there is a token under the cookie requesting the user interface. Refer to default.vue:

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

async and await come in pairs. Add async in front of the function name, and you can use await in the function to indicate that you must wait for the function following await to complete the execution and get the return result before continuing to execute the following code. Usually used to wait for the result of an asynchronous request to avoid callbacks. It's ok even without async.

In order to automatically add the token in the cookie to the headers for each request, we need to follow the steps below to write an interceptor for http requests.

2.2 http interceptor

When the HTTP interceptor functions, each time a request and data are returned, it automatically helps us handle some global public operations. For example, the authentication token here is added.

2.2.1 Create a new plugins/axios.js plugin file Implement the http interceptor through the axios plugin.

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 Configure and load the axios plugin in nuxt.config.js

plugins: ['@/plugins/element-ui', '@/plugins/axios'],

2.2.3 Adding a cookie plug-in Because we need to read cookies, we need to install a cookie plug-in for easy use.

yarn add cookie-universal-nuxt --save

nuxt.config.js

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

Then, it can be used directly in our plugins/axios.js and (default).vue.

Routing, add a home list page

nuxt.js does not need to write router.js, it is agreed that the vue file under pages is the route. Relatively easy. We add a new index.vue home page and change the original index.vue to workspace.vue. new 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 mainly includes: 1. getList to get the homepage list data; 2. this.$router.push({ path: '/workspace', query: { id: item.id } }) to open a specific page

finally

Since then, a small project with complete internal organs has been completed, including: framework construction, plug-ins, vuex, identity authentication, http communication, routing and other functions.

The function details of the whole project are not perfect, welcome everyone to mention pr:

Full details can be found at: http://topology.le5le.com/ , development documentation . Join the list of contributors! You are also welcome to join the group for discussion:topology technical discussion group 2

How to contribute

Through GitHub's pr method: 0. Read the development documentation to understand the relevant properties.

  1. fork the repository to your own name
  2. Modify locally and submit to your own git repository
  3. Find the "Pull request" button in your fork repository and submitfile

Open source projects are not easy, everyone is welcome to participate, give [ articles , GitHub open source libraries ] a star and like, or fund the server:
file

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324117017&siteId=291194637