接前面二,下面咱们实现右键菜单、http通讯、路由。html
本系列教程是用Vue.js + Nuxt.js + Element + Vuex + 开源js绘图库,打造一个属于本身的在线绘图软件,最终效果:http://topology.le5le.com 。若是你以为好,欢迎给文章和开源库点赞,让咱们更有动力去作好!vue
本系列教程源码地址:Githubnode
目录
- 搭建项目框架,教程一
- vuex,教程二
- 右键菜单,教程三
- http通讯,教程三
- 路由,教程三
右键菜单
右键菜单原理很简单:自定义html的oncontextmenu事件:ios
<div id="topology-canvas" class="full" @contextmenu="onContextMenu($event)"></div>
屏蔽默认右键菜单事件,计算右键鼠标位置,弹出一个咱们本身的div自定义菜单便可git
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实例和选中的属性数据github
props: { canvas: { type: Object, require: true }, props: { type: Object, require: true } }
其中,属性props含义为:vuex
props: { node: null, // 选中节点 line: null, // 选中连线 nodes: null, // 选中多个节点 multi: false, // 选中多个节点/连线 locked: false // 选中对象是否被锁定 }
而后,咱们根据菜单事件和属性props来调用canvas的相应接口函数,参考开发文档element-ui
http通讯
这里,咱们不去从零写个后端服务,直接采用topology.le5le.com线上接口服务。canvas
代理配置
首先,咱们须要给nuxt.config.js添加http代理配置,这样开发环境下的http请求,自动代理转发给topology.le5le.com,获取到真实数据。axios
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/ 换成 http://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 } }) 打开具体页面
最后
自此,一个麻雀虽小五脏俱全的小项目就完成了,包含:框架搭建、插件、vuex、身份认证、http通讯、路由等功能。
整个项目功能细节还不完善,欢迎你们提pr:
完整细节可参考:http://topology.le5le.com/ ,开发文档 。可加入贡献者名单哦!也欢迎加群交流讨论:
如何贡献
经过GitHub的pr方式: 0. 阅读开发文档,了解相关属性。
- fork仓库到本身名下
- 本地修改并提交到本身的git仓库
- 在本身的fork仓库找到 “Pull request” 按钮,提交