Ver código fonte

feat(iframe): iframe 页签嵌入外部网站

实现需求 #3
zhaihaoyi 6 anos atrás
pai
commit
05d4de7648

+ 1 - 0
README.md

@@ -73,6 +73,7 @@ yarn add vue-router-tab
 - [x] 内置页签和页面[过渡效果](docs/guide.md#过渡效果),支持自定义配置
 - [x] [自定义页签模板](docs/guide.md#自定义页签模板)
 - [x] [动态更新页签信息](docs/guide.md#动态更新页签) (标题/图标/提示)
+- [x] [iframe 页签嵌入外部网站](docs/guide.md#iframe-页签操作)
 - [x] [页签页面离开 (页签关闭/刷新/替换) 前确认](docs/guide.md#页面离开确认)
 - [x] [语言配置](docs/guide.md#语言配置):zh-CN (默认) / en,自定义语言
 

+ 2 - 1
demo/router.js

@@ -1,5 +1,6 @@
 import Vue from 'vue'
 import Router from 'vue-router'
+import { RouterTabRoutes } from '../src'
 
 const importPage = view => () => import(/* webpackChunkName: "p-[request]" */ `./views/${view}.vue`)
 
@@ -46,7 +47,7 @@ let pageRoutes = [{
     title: '页面离开确认',
     icon: 'rt-icon-contact'
   }
-}]
+}, ...RouterTabRoutes]
 
 export default new Router({
   routes: [{

+ 41 - 5
demo/views/Page.vue

@@ -8,6 +8,18 @@
 
     <h3>页签操作</h3>
 
+    <p>
+      <a
+        class="demo-btn"
+        @click="$routerTab.refresh()"
+      >刷新当前页面</a>
+
+      <a
+        class="demo-btn"
+        @click="$routerTab.close()"
+      >关闭当前页面</a>
+    </p>
+
     <p>
       <router-link
         class="demo-btn"
@@ -46,16 +58,40 @@
       >关闭“动态更新页签”</a>
     </p>
 
+    <h4>iframe 页签</h4>
+
     <p>
       <a
         class="demo-btn"
-        @click="$routerTab.refresh()"
-      >刷新当前页面</a>
+        @click="$routerTab.openIframeTab('https://www.baidu.com', '百度')"
+      >打开“百度”</a>
 
       <a
         class="demo-btn"
-        @click="$routerTab.close()"
-      >关闭当前页面</a>
+        @click="$routerTab.refreshIframeTab('https://www.baidu.com')"
+      >刷新“百度”</a>
+
+      <a
+        class="demo-btn"
+        @click="$routerTab.closeIframeTab('https://www.baidu.com')"
+      >关闭“百度”</a>
+    </p>
+
+    <p>
+      <a
+        class="demo-btn"
+        @click="$routerTab.openIframeTab('https://map.baidu.com/', '百度地图', 'rt-icon-web')"
+      >打开“百度地图”</a>
+
+      <a
+        class="demo-btn"
+        @click="$routerTab.refreshIframeTab('https://map.baidu.com/')"
+      >刷新“百度地图”</a>
+
+      <a
+        class="demo-btn"
+        @click="$routerTab.closeIframeTab('https://map.baidu.com/')"
+      >关闭“百度地图”</a>
     </p>
 
     <div>
@@ -90,7 +126,7 @@ export default {
 
   methods: {
     click () {
-      console.log('aaa')
+      console.log(`页面${this.pageId}`)
     }
   }
 }

+ 28 - 8
docs/api.md

@@ -106,9 +106,7 @@
   - `{String | Object} [location]` 路由地址 - [参考文档](https://router.vuejs.org/zh/guide/essentials/navigation.html#router-push-location-oncomplete-onabort)
   - `{Boolean} [fullMatch = true]` 是否全匹配(匹配fullPath去除hash部分)
 
-- **说明**:
-
-  关闭指定 `location` 的页签
+- **说明**:关闭指定 `location` 的页签
 
   
 ### routerTab.refresh
@@ -117,9 +115,7 @@
   - `{String | Object} [location]` 路由地址 - [参考文档](https://router.vuejs.org/zh/guide/essentials/navigation.html#router-push-location-oncomplete-onabort)
   - `{Boolean} [fullMatch = true]` 是否全匹配(匹配fullPath去除hash部分)
 
-- **说明**:
-
-  刷新指定 `location` 的页签
+- **说明**:刷新指定 `location` 的页签
 
 
 ### routerTab.refreshAll
@@ -127,9 +123,33 @@
 - **参数**:
   - `{Boolean} [force = false]` 如果 `force` 为 `true`,则忽略页面组件的 `beforePageLeave` 配置,强制刷新所有页签
 
-- **说明**:
+- **说明**:刷新所有页签
+
+
+### routerTab.openIframeTab
+
+- **参数**:
+  - `{String} [src]` 要打开的 iframe 页签链接
+  - `{String} [title]` 页签标题
+  - `{String} [icon]` 页签图标
+
+- **说明**:打开 iframe 页签
+
+
+### routerTab.closeIframeTab
+
+- **参数**:
+  - `{String} [src]` 要关闭的 iframe 页签链接
+
+- **说明**:关闭 iframe 页签
+
+
+### routerTab.refreshIframeTab
+
+- **参数**:
+  - `{String} [src]` 要刷新的 iframe 页签链接
 
-  刷新所有页签
+- **说明**:刷新 iframe 页签
 
 
 

+ 67 - 23
docs/guide.md

@@ -66,19 +66,23 @@ new Vue({
 </template>
 ```
 
-### 页签信息配置
+### 路由配置
 
-通过路由的 `meta` 信息,来设置页签的**标题**、**图标**、**提示**和**路由页签规则**
+1. 引入 `RouterTab` 内置路由以支持[操作 iframe 页签](#iframe-页签操作)
+2. 通过路由的 `meta` 信息,来设置页签的**标题**、**图标**、**提示**和**路由页签规则**
 
 配置参考: [Route.meta 路由元信息](api.md#route-meta-路由元信息)
 
 **示例:**
 
-``` javascript {5,6,17,18,21,22,23,24,25,26}
+``` javascript {6,9,22,25,28,33,34,35,36}
 // @/router.js 路由
 import Vue from 'vue'
 import Router from 'vue-router'
 
+// RouterTab 内置路由
+import { RouterTabRoutes } from 'vue-router-tab'
+
 // 引入布局框架组件
 import Frame from './components/layout/Frame.vue'
 
@@ -91,27 +95,38 @@ export default new Router({
   routes: [{
     path: '/',
     redirect: '/page1',
-    component: Frame, // 父路由组件内必须包含 <router-tab>
-    children: [{ // 子路由里配置需要通过页签打开的页面路由
-      path: '/page/:id',
-      component: importPage('Page'),
-      meta: {
-        title: '页面', // 页签标题
-        icon: 'icon-user', // 页签图标,可选
-        tips: '这是一个页面', // 页签提示,可选,如未设置则跟title一致
-        aliveId: 'fullPath', // 路由打开页签规则,可选
-      }
-    }, {
-      path: '/404',
-      component: importPage('404'),
-      meta: {
-        title: '找不到页面',
-        icon: 'icon-page'
+
+    // 父路由组件内必须包含 <router-tab>
+    component: Frame,
+
+    // 子路由里配置需要通过页签打开的页面路由
+    children: [
+
+      // 引入 RouterTab 内置路由以支持操作 iframe 页签
+      ...RouterTabRoutes,
+      {
+        path: '/page/:id',
+        component: importPage('Page'),
+        meta: {
+          title: '页面', // 页签标题
+          icon: 'icon-user', // 页签图标,可选
+          tips: '这是一个页面', // 页签提示,可选,如未设置则跟title一致
+          aliveId: 'fullPath', // 路由打开页签规则,可选
+        }
+      },
+      {
+        path: '/404',
+        component: importPage('404'),
+        meta: {
+          title: '找不到页面',
+          icon: 'icon-page'
+        }
+      },
+      {
+        path: '*',
+        redirect: '/404'
       }
-    }, {
-      path: '*',
-      redirect: '/404'
-    }]
+    ]
 }
 ```
 
@@ -238,6 +253,35 @@ this.$routerTab.refreshAll()
 this.$routerTab.refreshAll(true)
 ```
 
+### iframe 页签操作
+
+`RouterTab` 支持通过 iframe 页签嵌入外部网站。
+
+::: warning
+该功能需要引入 `RouterTab` 内置路由,请参考 [基础 - 路由配置](#路由配置)
+:::
+
+<doc-links api="#routertab-openiframetab" demo="/default/"></doc-links>
+
+**打开 iframe 页签**
+
+``` js
+// 三个参数分别为:链接、页签标题、图标
+this.$routerTab.openIframeTab('https://map.baidu.com/', '百度地图', 'icon-web')
+```
+
+**关闭 iframe 页签**
+
+``` js
+this.$routerTab.closeIframeTab('https://map.baidu.com/')
+```
+
+**刷新 iframe 页签**
+
+``` js
+this.$routerTab.refreshIframeTab('https://map.baidu.com/')
+```
+
 
 ## 页签规则
 

+ 1 - 1
public/index.html

@@ -6,7 +6,7 @@
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     <link rel="icon" href="<%= BASE_URL %>img/logo.png">
     <title>Vue Router Tab - Demo</title>
-    <link rel="stylesheet" href="//at.alicdn.com/t/font_1048253_c82vhdhsf2.css"/>
+    <link rel="stylesheet" href="//at.alicdn.com/t/font_1048253_ssze9920nn.css"/>
     <script>
     (function () {
       // 链接里带入参数debug=true以加载eruda移动端调试工具

+ 4 - 0
src/components/RouterTab.js

@@ -12,9 +12,13 @@ import langs from '../lang'
 // 子组件
 import RouterAlive from './RouterAlive'
 
+// 功能混入
+import iframe from '../mixins/routerTab/iframe'
+
 export default {
   name: 'RouterTab',
   components: { RouterAlive },
+  mixins: [ iframe ],
   props: {
     // 缓存id,如果为函数,则参数为route
     aliveId: RouterAlive.props.aliveId,

+ 15 - 0
src/components/RouterTab.vue

@@ -79,6 +79,21 @@
           />
         </transition>
       </router-alive>
+
+      <transition-group
+        v-bind="typeof pageTransition === 'string' ? { name: pageTransition } : pageTransition"
+        tag="div"
+        class="router-tab-iframes"
+      >
+        <iframe
+          v-for="url in iframes"
+          v-show="url === currentIframe"
+          :key="url"
+          :src="url"
+          frameborder="0"
+          class="router-tab-iframe"
+        />
+      </transition-group>
     </div>
 
     <!-- 右键菜单 -->

+ 4 - 0
src/index.js

@@ -1,5 +1,6 @@
 import RouterTab from './components/RouterTab.vue'
 import routerPage from './mixins/routerPage'
+import routes from './util/routes'
 
 // 安装
 RouterTab.install = function install (Vue, options) {
@@ -16,3 +17,6 @@ if (typeof window !== 'undefined' && window.Vue) {
 }
 
 export default RouterTab
+
+// 路由
+export const RouterTabRoutes = routes

+ 44 - 0
src/mixins/routerTab/iframe.js

@@ -0,0 +1,44 @@
+// iframe 页签功能混入
+export default {
+  data () {
+    return {
+      iframes: [],
+      currentIframe: null
+    }
+  },
+
+  methods: {
+    // 获取 iframe 页签路由路径
+    getIframePath (src, title = null, icon = null) {
+      let path = `${this.basePath}/iframe/${encodeURIComponent(src)}`
+
+      if (title) {
+        path += '/' + title
+
+        if (icon) {
+          path += '/' + icon
+        }
+      }
+
+      return path
+    },
+
+    // 打开 iframe 页签
+    openIframeTab (src, title, icon) {
+      let path = this.getIframePath(src, title, icon)
+      this.$router.push(path)
+    },
+
+    // 关闭 iframe 页签
+    closeIframeTab (src) {
+      let path = this.getIframePath(src)
+      this.close(path, false)
+    },
+
+    // 刷新 iframe 页签
+    refreshIframeTab (src) {
+      let path = this.getIframePath(src)
+      this.refresh(path, false)
+    }
+  }
+}

+ 48 - 0
src/page/Iframe.vue

@@ -0,0 +1,48 @@
+<template>
+  <div class="router-tab-iframe-fake" />
+</template>
+
+<script>
+export default {
+  name: 'Iframe',
+  props: {
+    src: String,
+    title: String,
+    icon: String
+  },
+  data () {
+    return {
+      routeTab: null
+    }
+  },
+  mounted () {
+    let { src, title, icon, $routerTab: $tab } = this
+    let { iframes } = $tab
+
+    this.routeTab = { title, icon }
+
+    if (!iframes.includes(src)) {
+      iframes.push(src)
+    }
+    $tab.currentIframe = src
+  },
+
+  activated () {
+    this.$routerTab.currentIframe = this.src
+  },
+
+  deactivated () {
+    this.$routerTab.currentIframe = null
+  },
+
+  destroyed () {
+    let { src } = this
+    let { iframes } = this.$routerTab
+    let index = iframes.indexOf(src)
+
+    if (index > -1) {
+      iframes.splice(index, 1)
+    }
+  }
+}
+</script>>

+ 8 - 0
src/scss/routerTab.scss

@@ -189,6 +189,14 @@ $color-primary: #409eff;
     background: #fff;
 		transition: all .4s ease-in-out;
   }
+
+  &-iframe {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+  }
 	
 	// 右键菜单
 	&-contextmenu {

+ 12 - 0
src/util/routes.js

@@ -0,0 +1,12 @@
+import Iframe from '../page/Iframe.vue'
+
+// 注入的路由
+export default [{
+  // iframe 路由
+  path: 'iframe/:src/:title?/:icon?',
+  component: Iframe,
+  props: true,
+  meta: {
+    aliveId: route => `iframe-${route.params.src}`
+  }
+}]