Vue implementiert Multi-Tab-Komponente

Vue implementiert Multi-Tab-Komponente

Um die Wirkung direkt zu sehen, wurde ein Rechtsklickmenü hinzugefügt, welches die Funktionen „Neu laden“, „Links schließen“, „Rechts schließen“ und „Weitere schließen“ umfasst.

Sie können sich den Code auch auf meinem GitHub ansehen (wenn Sie diese Komponente nützlich finden, vergessen Sie nicht, ihr einen Stern zu geben).

Code: https://github.com/Caijt/VuePageTab

Demo: https://caijt.github.io/VuePageTab/

Die Methode zum Löschen des Caches in meiner Multi-Tab-Komponente besteht nicht darin, die Einschluss- und Ausschlusskombination der Keep-Alive-Komponente zu verwenden, sondern die Brute-Force-Methode zum Löschen des Caches. Dies wurde auch im vorherigen Blog erwähnt. Mit dieser Methode kann eine umfassendere Multi-Tab-Funktion erreicht werden. Beispielsweise kann dieselbe Route gemäß unterschiedlichen Parametern gleichzeitig verschiedene Tabs öffnen, und es besteht keine Notwendigkeit, die Namenswerte dieser Routen zu schreiben.

Schauen wir uns zunächst den Komponentencode direkt an (darin werden einige Element-UI-Komponenten verwendet. Wenn Sie Element-UI nicht verwenden, können Sie diese entfernen und selbst implementieren).

<Vorlage>
 <div Klasse="__common-layout-pageTabs">
  <el-Bildlaufleiste>
   <div Klasse="__tabs">
    <div
     Klasse="__tab-item"
     v-for="Element in geöffneten Seitenroutern"
     :Klasse="{
      '__ist aktiv': item.fullPath == $route.fullPath,
     }"
     :Schlüssel="item.vollständigerPfad"
     @click="beimKlick(Element)"
     @contextmenu.prevent="showContextMenu($event, item)"
    >
     {{ item.meta.title }}
     <span
      Klasse="el-icon-close"
      @click.stop="beimSchließen(Element)"
      @contextmenu.prevent.stop=""
      :style="openedPageRouters.length <= 1 ? 'width:0;' : ''"
     ></span>
    </div>
   </div>
  </el-scrollbar>
  <div v-show="contextMenuSichtbar">
   <ul
    :style="{ links: contextMenuLeft + 'px', oben: contextMenuTop + 'px' }"
    Klasse="__Kontextmenü"
   >
    <li>
     <el-button Typ="Text" @click="reload()" Größe="mini">
      Neu laden</el-button>
    </li>
    <li>
     <el-Schaltfläche
      Typ="Text"
      @click="schließenAndereLinks"
      :deaktiviert="false"
      Größe="mini"
      >Schließen links</el-Button
     >
    </li>
    <li>
     <el-Schaltfläche
      Typ="Text"
      @click="AnderesRechtsschließen"
      :deaktiviert="false"
      Größe="mini"
      >Rechts schließen</el-Button
     >
    </li>
    <li>
     <el-button Typ="Text" @click="schließenAndere" Größe="mini"
      >Andere schließen</el-button
     >
    </li>
   </ul>
  </div>
 </div>
</Vorlage>
<Skript>
Standard exportieren {
 Requisiten: {
  keepAliveComponentInstance: {}, //Keep-Alive-Steuerinstanzobjekt blankRouteName: {
   Typ: Zeichenfolge,
   Standard: "leer",
  }, //leerer Routenname-Wert},
 Daten() {
  zurückkehren {
   contextMenuVisible: false, //Gibt an, ob das Rechtsklickmenü angezeigt wirdcontextMenuLeft: 0, //Anzeigeposition des RechtsklickmenüscontextMenuTop: 0, //Anzeigeposition des RechtsklickmenüscontextMenuTargetPageRoute: null, //Menüroute, auf die die rechte Maustaste zeigtopenedPageRouters: [], //Geöffnete Routenseiten};
 },
 betrachten:
  //Wenn sich die Route ändert, führe die Methode zum Öffnen der Seite aus $route: {
   handler(v) {
    diese.öffnenSeite(v);
   },
   sofort: wahr,
  },
 },
 montiert() {
  //Klick hinzufügen, um das Rechtsklickmenü zu schließen window.addEventListener("click", this.closeContextMenu);
 },
 zerstört() {
  window.removeEventListener("klicken", this.closeContextMenu);
 },
 Methoden: {
  //Öffne die Seite openPage(route) {
   wenn (route.name == dieser.blankRoutenname) {
    zurückkehren;
   }
   let isExist = this.openedPageRouters.some(
    (Element) => Element.vollständigerPfad == Route.vollständigerPfad
   );
   wenn (!istExistiert) {
    let openedPageRoute = this.openedPageRouters.find(
     (Element) => Element.Pfad == Routenpfad
    );
    // Überprüfen Sie, ob die Seite mehrere Seiten mit unterschiedlichen Parametern unterstützt. Wenn dies nicht der Fall ist und bereits eine Seitenroute mit demselben Pfadwert vorhanden ist, ersetzen Sie diese, wenn (!route.meta.canMultipleOpen && openedPageRoute != null) {
     this.delRouteCache(openedPageRoute.fullPath);
     dies.openedPageRouters.splice(
      this.openedPageRouters.indexOf(openedPageRoute),
      1,
      Route
     );
    } anders {
     dies.openedPageRouters.push(route);
    }
   }
  },
  //Klicken Sie auf die Registerkarte Seite onClick(route) {
   wenn (route.fullPath !== this.$route.fullPath) {
    dies.$router.push(route.fullPath);
   }
  },
  //Seitentab schließen onClose(route) {
   let index = this.openedPageRouters.indexOf(route);
   diese.delPageRoute(route);
   wenn (route.fullPath === diese.$route.fullPath) {
    //Nach dem Löschen der Seite zur vorherigen Seite springen this.$router.replace(
     this.openedPageRouters[index == 0 ? 0 : index - 1]
    );
   }
  },
  //Rechtsklick, um das Menü anzuzeigenshowContextMenu(e, route) {
   this.contextMenuTargetPageRoute = Route;
   this.contextMenuLeft = e.layerX;
   dies.contextMenuTop = e.layerY;
   Dies.contextMenuVisible = true;
  },
  //Rechtsklickmenü ausblenden closeContextMenu() {
   this.contextMenuVisible = falsch;
   this.contextMenuTargetPageRoute = null;
  },
  //Seite neu laden reload() {
   dies.delRouteCache(dieses.contextMenuTargetPageRoute.fullPath);
   wenn (diese.contextMenuTargetPageRoute.fullPath === diese.$route.fullPath) {
    dies.$router.replace({ name: this.blankRouteName }).then(() => {
     dies.$router.replace(diese.contextMenuTargetPageRoute);
    });
   }
  },
  //Andere Seiten schließen closeOther() {
   für (lass i = 0; i < this.openedPageRouters.length; i++) {
    lass r = this.openedPageRouters[i];
    wenn (r !== diese.contextMenuTargetPageRoute) {
     diese.delPageRoute(r);
     ich--;
    }
   }
   wenn (diese.contextMenuTargetPageRoute.fullPath != diese.$route.fullPath) {
    dies.$router.replace(diese.contextMenuTargetPageRoute);
   }
  },
  //Den Index basierend auf dem Pfad abrufen getPageRouteIndex(fullPath) {
   für (lass i = 0; i < this.openedPageRouters.length; i++) {
    wenn (this.openedPageRouters[i].fullPath === fullPath) {
     gebe ich zurück;
    }
   }
  },
  //Linke Seite schließen closeOtherLeft() {
   let index = this.openedPageRouters.indexOf(
    this.contextMenuZielseiteRoute
   );
   Lassen Sie currentIndex = this.getPageRouteIndex(this.$route.fullPath);
   wenn (Index > aktuellerIndex) {
    dies.$router.replace(diese.contextMenuTargetPageRoute);
   }
   für (sei i = 0; i < Index; i++) {
    lass r = this.openedPageRouters[i];
    diese.delPageRoute(r);
    ich--;
    Index--;
   }
  },
  //Die rechte Seite schließen closeOtherRight() {
   let index = this.openedPageRouters.indexOf(
    this.contextMenuZielseiteRoute
   );
   Lassen Sie currentIndex = this.getPageRouteIndex(this.$route.fullPath);
   für (lass i = Index + 1; i < this.openedPageRouters.length; i++) {
    lass r = this.openedPageRouters[i];
    diese.delPageRoute(r);
    ich--;
   }
   wenn (Index < aktuellerIndex) {
    dies.$router.replace(diese.contextMenuTargetPageRoute);
   }
  },
  //Seite löschen delPageRoute(route) {
   let routeIndex = this.openedPageRouters.indexOf(route);
   wenn (routeIndex >= 0) {
    dies.openedPageRouters.splice(routeIndex, 1);
   }
   dies.delRouteCache(route.fullPath);
  },
  //Seitencache löschen delRouteCache(key) {
   lass cache = this.keepAliveComponentInstance.cache;
   Lassen Sie die Schlüssel = this.keepAliveComponentInstance.keys;
   für (lass i = 0; i < Schlüssel.Länge; i++) {
    wenn (Schlüssel[i] == Schlüssel) {
     Schlüssel.spleißen(i, 1);
     if (cache[Schlüssel] != null) {
      Cache[Schlüssel] löschen;
     }
     brechen;
    }
   }
  },
 },
};
</Skript>
<style lang="scss">
.__common-layout-pageTabs {
 .__Kontextmenü {
  // Breite: 100px;
  Rand: 0;
  Rand: 1px durchgezogen #e4e7ed;
  Hintergrund: #fff;
  Z-Index: 3000;
  Position: absolut;
  Listenstiltyp: keiner;
  Polsterung: 5px 0;
  Rahmenradius: 4px;
  Schriftgröße: 14px;
  Farbe: #333;
  Kastenschatten: 1px 1px 3px 0 rgba (0, 0, 0, 0,1);
  li {
   Rand: 0;
   Polsterung: 0px 15px;
   &:schweben {
    Hintergrund: #f2f2f2;
    Cursor: Zeiger;
   }
   Taste {
    Farbe: #2c3e50;
   }
  }
 }

 $c-tab-border-color: #dcdfe6;
 Position: relativ;
 &::vor {
  Inhalt: "";
  Rahmen unten: 1px durchgehend $c-tab-border-color;
  Position: absolut;
  links: 0;
  rechts: 0;
  unten: 0;
  Höhe: 100%;
 }
 .__tabs {
  Anzeige: Flex;
  .__tab-item {
   Leerzeichen: Nowrap;
   Polsterung: 8px 6px 8px 18px;
   Schriftgröße: 12px;
   Rand: 1px durchgezogen $c-tab-border-color;
   Rand links: keiner;
   Rahmen unten: 0px;
   Zeilenhöhe: 14px;
   Cursor: Zeiger;
   Übergang: Farbe 0,3 s kubisch-bezier(0,645, 0,045, 0,355, 1),
    Polsterung 0,3 s kubisch-bézier (0,645, 0,045, 0,355, 1);
   &:erstes-Kind {
    Rahmen links: 1px durchgezogen $c-tab-border-color;
    Rahmen oben links – Radius: 2px;
    Rand links: 10px;
   }
   &:letztes-Kind {
    Rahmen oben rechts – Radius: 2px;
    Rand rechts: 10px;
   }
   &:nicht(.__ist-aktiv):hover {
    Farbe: #409eff;
    .el-icon-schließen {
     Breite: 12px;
     Rand rechts: 0px;
    }
   }
   &.__ist-aktiv {
    Polsterung rechts: 12px;
    Rahmen unten: 1px durchgezogen #fff;
    Farbe: #409eff;
    .el-icon-schließen {
     Breite: 12px;
     Rand rechts: 0px;
     Rand links: 2px;
    }
   }
   .el-icon-schließen {
    Breite: 0px;
    Höhe: 12px;
    Überlauf: versteckt;
    Randradius: 50 %;
    Schriftgröße: 12px;
    Rand rechts: 12px;
    Transform-Ursprung: 100 % 50 %;
    Übergang: alle 0,3 s kubisch-bézier (0,645, 0,045, 0,355, 1);
    vertikale Ausrichtung: Text oben;
    &:schweben {
     Hintergrundfarbe: #c0c4cc;
     Farbe: #fff;
    }
   }
  }
 }
}
</Stil>

Diese Komponente erfordert zwei Eigenschaften, eine ist keepAliveComponentInstance (Keep-Alive-Steuerungsinstanzobjekt), blankRouteName (leerer Routenname)

Warum brauche ich das Keep-Alive-Control-Instanzobjekt? Weil dieses Objekt zwei Eigenschaften hat, eine ist Cache und die andere sind Schlüssel, die die Keep-Alive-Cache-Daten speichern. Mit diesem Objekt kann ich den Cache manuell löschen, wenn die Registerkarte geschlossen wird. Wie erhält man dieses Objekt? Wie unten gezeigt, erhält man es beim gemounteten Ereignis auf der übergeordneten Seite, auf der sich Keep-Alive befindet (wenn sich Keep-Alive und die mehrseitige Registerkartenkomponente nicht auf derselben übergeordneten Seite befinden, müssen Sie möglicherweise Vuex verwenden, um den Wert zu übergeben).

<Vorlage>
 <div id="app">
  <page-tabs :keep-alive-component-instance="keepAliveComponentInstance" />
  <div ref="keepAliveContainer">
   <am Leben erhalten>
    <router-view :key="$route.fullPath" />
   </am Leben erhalten>
  </div>
 </div>
</Vorlage>

<Skript>
importiere PageTabs aus "./components/pageTabs.vue";
Standard exportieren {
 Name: "App",
 Komponenten:
  Seitentabs,
 },
 montiert() {
  wenn (this.$refs.keepAliveContainer) {
   this.keepAliveComponentInstance = this.$refs.keepAliveContainer.childNodes[0].__vue__; //Holen Sie sich das Keep-Alive-Steuerungsinstanzobjekt}
 },
 Daten() {
  zurückkehren {
   keepAliveComponentInstance: null,
  };
 }
};
</Skript>

Wofür ist der Name der leeren Route? Hauptsächlich möchte ich die Funktion zum Aktualisieren der aktuellen Seite realisieren. Wir wissen, dass Vue das Springen zur aktuellen Seite nicht zulässt. Daher möchte ich zuerst zu einer anderen Seite springen und dann zur Seite zurückspringen, um den Aktualisierungseffekt zu erzielen. (Natürlich verwende ich relpace, sodass keine Verlaufsdatensätze generiert werden)

Hinweis: Diese leere Route ist nicht auf die Stammroute festgelegt. Sie hängt vom Speicherort der Multi-Tab-Komponente ab. Wenn Sie eine Root-Router-Ansicht und eine Layout-Komponente haben, die auch eine untergeordnete Router-Ansicht hat, und sich die Multi-Tab-Komponente in dieser Layout-Komponente befindet, muss die leere Route in den untergeordneten Elementen der Route definiert werden, die der Layout-Komponente entspricht.

Diese Komponente wird je nach Metaobjekt des Routing-Objekts auch unterschiedlich konfiguriert, wie unten gezeigt

let router = neuer Router({
 Routen: [
  //Dies ist eine leere Seite. Beim Neuladen der aktuellen Seite wird { verwendet.
   Name: "leer",
   Pfad: "/blank",
  },
  {
   Pfad: "/a",
   Komponente: A,
   Meta: {
    Titel: "Seite A", //Seitentitel canMultipleOpen: true //Unterstützt das Öffnen mehrerer Tabs basierend auf unterschiedlichen Parametern. Wenn Sie zwei Tabs für /a bzw. /a?v=123 öffnen müssen, setzen Sie es bitte auf true, andernfalls wird nur ein Tab angezeigt und der später geöffnete Tab ersetzt den zuvor geöffneten Tab.}
  }
}

Oben sind die Details der Implementierung von Multi-Tab-Komponenten durch Vue aufgeführt. Weitere Informationen zur Implementierung von Multi-Tab-Komponenten durch Vue finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Ausführliche Erläuterung der Vue-Komponente „Multi-Select-Liste“
  • Probleme und Lösungen bei der Verwendung des V-Modells zum bidirektionalen Binden der Werte von übergeordneten und untergeordneten Komponenten in Vue
  • Vue-Beispielcode mit dem Animationseffekt „Übergangskomponente“
  • Zusammenfassung der Grundlagen der Vue-Komponenten
  • Tiefgreifendes Verständnis der dynamischen und asynchronen Komponenten von Vue
  • Schritte für Vue zur Verwendung von Ref, um Komponenten über Ebenen hinweg zu erhalten
  • Implementieren Sie ganz einfach den gesamten Prozess der Switch-Funktionskomponenten in vue3
  • So passen Sie Dialog- und Modalkomponenten in Vue3 an
  • Richtige Methode, um eine erneute Darstellung einer Komponente in Vue zu erzwingen
  • Zwei Möglichkeiten zum dynamischen Erstellen von Komponenten in Vue
  • Detaillierte Erklärung inkompatibler Änderungen von Komponenten in vue3

<<:  So verwenden Sie die HTML 5 Drag & Drop-API in Vue

>>:  Ausführliche Erklärung dieses Schlüsselworts in JavaScript

Artikel empfehlen

Eine kurze Analyse der Crontab-Aufgabenplanung in Linux

1. Erstellen Sie eine Planungsaufgabe Anweisung c...

Lösen Sie das Problem des Syn Flooding in der MySQL-Datenbank

Der SYN-Angriff ist die häufigste und am leichtes...

Detailliertes Tutorial zum Herunterladen und Installieren von mysql8.0.21

Offizielle Website-Adresse: https://www.mysql.com...

Detaillierte Erläuterung der Nginx-Anti-Hotlink- und Anti-Crawler-Konfiguration

Erstellen Sie eine neue Konfigurationsdatei (gehe...

So verwenden Sie Docker-Compose zum Erstellen eines ELK-Clusters

Auf alle Orchestrierungsdateien und Konfiguration...

Unterschiede im Stundentrennzeichen zwischen Browsern

Beim Erstellen einer Webseite verwenden Sie manchm...

Detailliertes Tutorial zur Installation von phpMyAdmin unter Ubuntu 18.04

Wir werden phpMyAdmin installieren, damit es mit ...

Der neue TypeScript-Schnellstart-Übungsbericht des Partners Vue

Inhaltsverzeichnis 1. Bauen Sie mit dem offiziell...