Die nativen Tabellenkomponenten von Bootstrap können nur die einfache Datenanzeige erfüllen und können nicht mehr betriebliche Anforderungen erfüllen. Natürlich finden Sie eine Bootstrap-basierte Tabellenkomponente namens "DataTables-1.10.11". Wenn Sie jedoch nicht viel über die API wissen, ist es schmerzhaft zu verwenden. Wenn Sie sich jedoch für JQGrid entscheiden, bringt Ihnen dieses Tutorial eine Lösung für diese operative Tabelle.
1. Effektanzeige
Ok, zeig einfach dieses Bild. Ich glaube, Sie haben sich in die Bootstrap -Version von JQGrid verliebt. Es ist sehr kompatibel mit Bootstrap und einfach perfekt. Dies erfordert natürlich einige Änderungen an der JQGrid des Vernunft und zur Einkapselung der Komponenten.
2. Ressourcen -Download
Wie auch immer, ich liebe es zu teilen. Sie können den Komponentencode von JQGRID von der offiziellen Website von JQGrid herunterladen. Nach dem Herunterladen sind jedoch einige Änderungen erforderlich. Dann lade ich die geänderte JQGrid direkt in Git hoch und Sie müssen die bereitgestellten Dateien nur in Ihr entsprechendes Projekt importieren.
Außerdem müssen Sie auch einen jQuery-ui-1.10.0.custom.css herunterladen, und ich werde die Download-Adresse nicht angeben, aber ich glaube, Sie werden sie auf jeden Fall finden. Selbst wenn Sie ein Baidu -Mädchen verwenden, das häufig Unfälle hat, können Sie es finden.
3. Darum geht es in diesem Artikel
Seit ich die QQ -Gruppe erstellt habe, haben sich einige Studenten der Gruppe in einem "unaufhörlichen Stream" angeschlossen, aber ich stellte auch fest, dass der erste Schritt in der Gruppe zu mir kam, um nach Demo- oder Projektcode zu bitten. Ich mag das nicht. Tun Sie es selbst, implementieren Sie Folgendes und verwandeln Sie es, und es wird Ihr eigenes Ding sein. Sie werden offensichtlich nicht mehr Hilfe bekommen, wenn Sie meinen Code vollständig kopieren. Ich hoffe, dass die oben genannten Schüler die Initiative ergreifen, wenn sie studieren.
Nachdem wir den obigen kleinen Unsinn gesagt haben, kehren wir wieder auf den Punkt und sprechen wir darüber, worüber unser Blog hauptsächlich spricht und was der Schlüssel zum Einbetten von JQGrid in Bootstrap ist. Ich fasse es wie folgt zusammen:
JQGrid Layout -Schema in Bootstrap JQGrids eigenen konstruktiven Parametern JQGrids modularer JQGrid -Datenoperation in Bootstrap
Es ist vorläufig in die oben genannten Teile unterteilt, um zu veranschaulichen, aber es muss beachtet werden, dass im Blog aufgrund von Platzbeschränkungen nur Ideen und ein gewisser Code bereitgestellt werden.
①. Der Layoutplan von JQGrid in Bootstrap
<!DOCTYPE html><html lang="zh-CN"><%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><%@ include file="/components/common/taglib.jsp"%><%@ include file = "/components/Common/csslib.jsp"%> <kopf> <link type = "text/css" rel = "styleSheet" href = "$ {ctx} /css/deal/my_pay_list.css"/> </head> <body> <div> <form id = "jqGridform". Action = "$ {CTX}/Deal/DataablePayDealorderList" methode = "post"> <div> <label für = "name"> Projektname: </Label> <div> <Eingabe type = "Text" name = "name" id = "name" placeholder = "plaptehy" passy "value =": id = "searchBtn"> such </button> </div> </div> <div> <table id = "pageGrid" rel = "jqGridform"> </table> <div id = "pageGridPager"> </div> </div> </form> </div> <%@ incluption = componentents/constroms/jslib.jsp. src = "$ {ctx} /js/deal/my_pay_list.js"> </script> </body> </html>Die Projekte jeder Person variieren stark. Im aufgeführten Code konzentrieren wir uns nur auf den Abschnitt JQGRID:
id="jqgridForm" , hier haben wir ein Formularformular mit einer Ebene von Suchbedingungen für JQGrid, dh den in den Renderings aufgeführten Suchenteil. Beim Klicken auf die Suchschaltfläche werden die Abfragebedingungen des Formularsformularfelds an den Controller übermittelt und die erhaltenen Daten erhalten. id="searchBtn" definiert die Suchschaltfläche, die später in der Modularität verwendet wird. <table id="pageGrid" rel="jqgridForm"></table> <div id="pageGridPager"></div> Definieren Sie das Tabellenelement von JQGrid und das Fußzeilenelement von JQGrid. Die von meinem Projekt verwendeten Regeln stimmen dieser Regel vorübergehend überein, und Sie können auch Ihre eigenen Regeln haben. Durch Angeben der ID des Formulars durch REL ist es einfacher, Tabellenabrufformular zu verwenden. ②, JqGrids eigene konstruktive Parameter
Als ich die Parameter aufbaute, habe ich sie in ① in my_pay_list.js extrahiert.
$(function() { var jqOption = { datatype : "xml", mtype : "POST", shrinkToFit : true, viewrecords : false, rownumbers : false, autowidth : true, height : "100%", colNames : [ 'id', 'status', 'project information', 'project status', 'order number', 'project name', 'order time', 'payment amount', 'supported Menge ',' Order Status ',' Operation '], Colmodel: [{Name:' ID ', Index:' ID ', Hidden: True}, {Name:' Status ', Index:' Status ', Hidden: True}, {Name:' Image_Str ', Index:' Image_str ', widdh. (cellValue == 'Gesamtzahlungskosten:') {return cellValue; ParseInt ($ (RowObject) .Find ("Status"). Text ()); {return '<a target = "_ leer" href = "' + Common. ], xmlReader: {repepitems: false, root: "pagegrid", zeile: "map", Seite: 'Seite', Gesamt: 'Total', Records: 'Records', ID: 'ID'}, Rownum: 50, RowList: [50, 100, 200, 300], Pager: #pageGridpager, Footherror, Footherror, Footherror, “, True: yunror: yunrorror (), Foothrow: True: TrueRor: yunror: {var $ form = $ ("#" + $ ("#pagegrid"). attr ("rel"); $ ("#pagegrid"). footerdata ("set", {image_str: "Gesamtzahlung:", order_price: JSON.Message}); } // dialog if ($ .fn.ajaxtodialog) {$ ("a [target = dialog]", $ ("#pageGrid")). Ajaxtodialog ();Studenten, die mit JQGrid nicht vertraut sind, empfehlen, JQGrids Demo und die offiziellen Dokumente von JQGrid zuerst zu lesen. Für Studenten, die bereits mit JQGrid vertraut sind, sind DOC und Demo definitiv ein Muss.
In der obigen Datei sind viele Attribute aufgeführt. Ich werde JqGrid nicht zu sehr vorstellen. Das Hauptthema dieses Artikels ist es, vorzustellen, wie JQGrid in Bootstrap einbettet. Der entscheidende Punkt besteht nicht darin, JQGrid einzuführen. Ich werde nur ein paar wichtige Punkte einführen:
formatter: function(cellvalue, options, rowObject) { , Formatierer werden immer noch sehr oft verwendet, daher ist es sehr wichtig, den Wert der entsprechenden Zelle zu erhalten. Mein JQGrid verwendet das Datenformat XML (Datentyp: "XML"), sodass Sie den Wert der entsprechenden Deal_id -Spalte über $(rowObject).find("deal_id").text() finden können. xmlReader : { repeatitems : false, root : "PageGrid", achten Sie auf die Parameterwerte in XMLReader. In der nächsten Einführung wird die Datenbetrieb von JQGRID ausführlich eingeführt, was sich auf die Hintergrund -XML -Dateneinkapselung bezieht. $("#pageGrid").footerData("set", {image_str : "支付总花费:", order_price : json.message}); In Bezug auf die Footerdata -Methode ist es auch sehr bequem zu verwenden, und der Effekt kann als Effekt bezeichnet werden. initEnv(jqOption); Verfahren. Nach dem Onload der Seite übergeben wir die Initialisierungsparameter von JQGRID an die Initenv -Methode. Bei der anschließenden Modularisierung von JQGrid in Bootstrap werden wir die InitenV -Methode einführen. ③, JQGrid -Modularität in Bootstrap
In ② haben wir die InitenV -Methode festgestellt, sodass diese Methode mit modularen Kapselungsarbeiten speziell für JQGrid erstellt wurde.
Initenv -Methode
function initenv (jQoption) {$ (Fenster) .Resize (function () {initLayout ();}); initui (null, jqoption);}Bei dieser Methode sehen wir die InitLayout -Methode und die InitUi -Methode, und der spezifische Inhalt wird später eingeführt.
initlayout
Funktion initLayout () {$ ("Tabelle [rel = jqGridForm]"). Jede (Funktion () {var rel = $ (this) .attr ("rel"); if (rel) {var $ form = $ ("#" + rel); var tablewidth = $ form.width ();Das heißt, wenn das Fenster skaliert ist, zeichnen wir die Breite für JQGrid erneut, um sich an Bootstraps reaktionsschnelle Layout anzupassen. Die verwendete Methode ist die setGridwidth -Methode von JQGrid.
initui
function initUi (_box, jQoption) {var $ p = $ (_ Box || document); if (jQoption) {yunm.debug ("initializejqGrid"); var $ form = $ ("#" + $ ("#pagegrid"). attr ("rel")); Yunm.debug (yunm.array2obj ($ form.serializearray ())); // initialisieren var op = $ .extend ({url: $ form.attr ("action"), postdata: yunm.array2Obj ($ form.Serializearray ()),}, jQoption); $ ("#pagegrid"). JqGrid (op); // Suchstast $ ("#searchBtn", $ form) .click (function () {$ ("#pagegrid"). $ ("#pagegrid"). Trigger ("reloadgrid"); // Symbolleiste, entfernen Sie die abgerundete Ecke der Taste $ (". Btn", $ form) .each (function () {var $ this = $ (this); }} Array2OBJ: function (array) {var params = $ ({}); $ .each (Array, Funktion (i) {var $ param = $ (this) [0]; params.attr ($ param.name, $ param.Value);}); Rückgabeparameter [0];},Wenn Sie meine vorherige Artikelserie gelesen haben, sind Sie mit der Initui -Methode nicht zu unbekannt. Freunde, die mit DWZ vertraut sind, werden natürlich nicht damit nicht vertraut sein. Die meisten Vorlagen in meinem Projekt beruhen immer noch auf DWZ. Vielen Dank an diese Senioren.
var $form = $("#" + $("#pageGrid").attr("rel")); Da wir die Formularformularbedingung mit JQGrid in Verbindung bringen, können wir hier das Formularformobjekt erhalten. Wenn wir das Formularformobjekt erhalten, erhalten wir natürlich den Wert des Suchfeldes ( $form.serializeArray() ). Nachdem Sie den Suchfeldwert des Formularsformulars erhalten haben, müssen Sie zu diesem Zeitpunkt eine Verarbeitung durchführen. Wir wissen, dass JQGrid, wenn es Parameter an Controller übergeht, bei Paging und sortierten zugehörigen Feldern (Seite, Zeilen, Sord, SIDX) gesendet werden muss. Die verwendete Methode ist $("#pageGrid").jqGrid({postData:xxx}); Normalerweise müssen wir beim Senden von Formularformularen nur $form.serializeArray() verwenden. Wenn wir jedoch zu diesem Zeitpunkt XXX durch $form.serializeArray() ersetzen, werden die paging- und sortierten verwandten Felder (Seite, Zeilen, Sord, SIDX) nicht in Controller erhalten. Dies ist ein Konflikt. Wie geht es zu dieser Zeit damit um? Die Lösung besteht var op =$.extend({url:$form.attr("action"),postData:YUNM.array2obj($form.serializeArray()),},jqOption);$("#pageGrid").jqGrid(op); , die Formularformulardaten zu objektivieren var op =$.extend({url:$form.attr("action"),postData:YUNM.array2obj($form.serializeArray()),},jqOption);$("#pageGrid").jqGrid(op); $("#searchBtn", $form).click Reloads JQGrid -Daten durch Einkapselung des Click -Ereignisses. $(".btn", $form).each(function() { Die Methode hier wird die Taste abrufen
Ich glaube, der Teil des Datenbetriebs umfasst das Übergeben von Suchparametern, die Pagiersortparameterübergabe und das Schreiben von SQL -Anweisungen.
In Bezug auf das Passieren von Parametern wurde in ③ die Front-End-Parametereinkapselung eingeführt. Schauen wir uns an, wie die Daten im Controller verarbeitet werden.
Lassen Sie uns zunächst PageGrid definieren, der Datenquelle von XMLreader in JQGrid ist.
Paket com.honzh.common.page; import java.util.list; import com.thoughtworks.xstream.Annotations.xstreamalias; @xstreamalias ("pagegrid")@unterdrückungswarnings ("rawtypes") öffentliche Klassenpagegrid {private int page; privat int insgesamt; private INT -Aufzeichnungen; private Listendaten; public int getPage () {return this.page; } public void setPage (int page) {this.page = page; } public int getTotal () {return this.total; } public void settotal (int insgesamt) {this.total = Total; } public int getRecords () {return this.records; } public void setRecords (int records) {this.Records = records; } public list getData () {return this.data; } public void setData (Listendaten) {this.data = data; }}Xstream.jar ist im Projekt erforderlich und laden Sie es selbst herunter.
Xstreamcomponent.java
Paket com.honzh.common.page; import org.apache.commons.lang.Stringutils; import com.thoughtworks.xstream.xstream; import Com.thoughtworks.xstream.Converters.Converter; Com.thoden.xstream.ioMl.Ml.DioDriver; com.thoughtworks.xstream.mapper.xstream11xmlfriendlYMapper; öffentliche Klasse XstreamComponent {private xstream xstream; public static xstreamComponent newInstance () {xstreamComponent xmlComponent = new XstreamComponent (); xmlcomponent.alias (neue Klasse [] {pagegrid.class}); return xmlcomponent; } public xstreamComponent () {this.xstream = new Xstream (neuer Domdriver ()); } public String toxml (Objekt obj) {return this.xstream.toxml (obj); } public String topagexml (Objekt obj) {RegisterConverter (neuer MAPCUSTOMCONVERTER (neuer defaultMapper (xstream11xmlfriendlyMapper.class.getClassloader ()))); retourml (obj); } öffentliches Objekt von Pagexml (String xml) {RegisterConverter (neuer MAPCUSTOMCONVERTER (neuer defaultMapper (xstream11xmlFriendlyMapper.class.getClassloader ()))); return fromxml (xml); } public Object vonxml (String xml) {return this.xstream.fromxml (xml); } @SuppressWarnings ("rawtypes") public void Processannotations (Klassentyp) {this.xstream.processannotations (Typ); } @SuppressWarnings ("rawtypes") public void processannotations (class [] Typen) {this.xstream.ProcessAnnotations (Typen); } @SuppressWarnings ("rawtypes") public void alias (String -Name, Klassentyp) {this.xstream.alias (Name, Typ); } @SuppressWarnings ("rawtypes") public void alias (class [] Typen) {für (Klassentyp: Typen) {String className = type.getName (); try {string [] classNames = stringutils.Split (className, "."); this.xstream.alias (classNames [(classNames.length - 1)], Typ); } catch (Ausnahme ex) {this.xstream.alias (className, type); }}} public void RegisterConverter (Converter Converter) {this.xstream.registerConverter (Converter); } @Suppresswarnings ("rawttypes") public void useeTtributefor (Klasse definedin, String fieldname) {this.xstream.Useattributefor (definedin, fieldname); }}Das PageGrid ist hauptsächlich als XML -Objekt eingekapselt und dann an das vordere Ende übergeben.
Mapcustomconverter.java
Paket com.honzh.common.page; import Java.util.hashMap; Import Java.util.hashtable; Import Java.util.iterator; import Java.util.map; com.thodensWorks.xstream.Converters.MarshallingContext; com.thoughtworks.xstream.io.HierarchicalStreamReader;import com.thoughtworks.xstream.io.HierarchicalStreamWriter;import com.thoughtworks.xstream.mapper.Mapper;public class MapCustomConverter extends AbstractCollectionConverter { public MapCustomConverter(Mapper mapper) { super(mapper); } @Suppresswarnings ("rawttypes") public boolean canconvert (Klassentyp) {return (type.equals (hashmap.class)) || (Typ.Equals (Hashtable.Class)) || (Type.GetName (). Equals ("java.util.linkedhashMap")) || (type.getName (). Equals ("sun.font.attributemap")); } @SuppressWarnings ({"rawtypes"}) public void Marschall (Objektquelle, HierarchicalStreamWriter -Schriftsteller, MarshallingContext -Kontext) {MAP MAP = (MAP) Quelle; für (iterator iterator = map.entrySet (). iterator (); iterator.hasnext ();) {map.Entry -Eintrag = (map.Entry) iterator.next (); Writer.StartNode (Entry.getKey () == NULL? "NULL": Eintrag.getKey (). ToString ()); writer.setValue (Entry.getValue () == null? "": Eintrag.getValue (). ToString ()); writer.endnode (); }} @Suppresswarnings ("rawtypes") öffentliches Objekt Unmarshal (HierarchicalStreamReader Reader, UnmarshallingContext -Kontext) {MAP MAP = (MAP) CreateCollection (context.getRequiredType ()); populatemap (Leser, Kontext, Karte); Rückgabekarte; } @Suppresswarnings ({"rawtypes", "Unbekämpft"}) Protected void populatemap (HierarchicalStreamReader -Leser, UnmarshallingContext -Kontext, Karte map) {while (reader.hasmorechildren ()) {maper.Movedown (); Object Key = reader.getNodename (); Object value = reader.getValue (); map.put (Schlüssel, Wert); Reader.MoveUp (); }}}Es konvertiert hauptsächlich HashMap, die in der Datenbank abgerufen werden, in Standard -XML -Formatdaten.
Baseconditionvo.java
Paket com.honzh.common.Persistence; import Java.util.hashMap; Import Java.util.map; 2.Pagenum - welche Seite. <br> * 3.Numperpage-wie viel zeigt eine Seite? Wenn es leer ist, wird page_show_count angezeigt. <br> * 4.TotalCount - Total -Nummer. TotalCount/NumperPage = Wie viele Seiten <br> * 5.Orderfield wortes teortierte Spalten. <br> * 6.OrderDirektion - die Sortierrichtung. * </P> */öffentliche Klasse BaseConditionvo {public Final static int page_show_count = 50; privates int pagenum = 1; private int numperPage = 0; private long TotalCount = 0; private String orderfield = ""; private String orderDirection = ""; /*** @fields PS: Kapselungsparametertypen. */ private map <String, Objekt> Mo = New HashMap <String, Object> (); public int getPagenum () {return pagenum; } public void setpagenum (int pagenum) {this.pagenum = pagenum; } public int getNumPerPage () {return numperpage> 0? numperpage: page_show_count; } public void setNumPage (int numperPage) {this.numperpage = numperPage; } public String getorderfield () {return orderfield; } public void setOrderfield (String orderfield) {this.orderfield = orderfield; } public String getOrderDirection () {return "Desc" .Equals (OrderDirection)? "Desc": "ASC"; } public void setOrderDirection (String orderDirection) {this.orderDirection = orderDirection; } public Long GettotAlcount () {return TotalCount; } public void settotalCount (langes TotalCount) {this.totalCount = TotalCount; } public int getStartIndex () {int pagenum = this.getPagenum ()> 0? this.getPagenum () - 1: 0; return pagenum * this.getNumPerPage (); } public rowbounds createRowBounds () {rowbounds ro = new rowbounds (this.getStartIndex (), this.getNumPage ()); return ro; } / ** * @title: addParams * @Description: Abfragebedingungen hinzufügen * @param key * @param value * / public void addParams (String -Schlüssel, Objektwert) {this.getMo (). Put (Schlüssel, Wert); } / ** * @title: getParams * @Description: Abfragebedingungen * @param key * @return * / public Object getParams (String -Schlüssel) {return this.getMo (). Get (key); } / ** * @return die mo * / public map <string, Object> getMo () {return Mo; } / ** * @param mo * Das MO zum Setzen * / public void setMo (MAP <String, Object> Mo) {this.mo = mo; } @Override public String toString () {return "Bedingung:" + pagenum + "," + numperPage + "," + TotalCount + "," + orderfield + "," + orderDirection + "," + mo; }}Das Abfragedatenobjekt für Pagination, einschließlich Paging-, Sortier- und Suchfelder.
geschützte Baseconditionvo getBaseConditionVOFROTABLE () {BaseConditionvo vo = new Baseconditionvo (); // Paginationsparameter int currentPage = getParatoint ("Seite"); int Größen = getParatoint ("Zeilen"); String sortOrder = getPara ("Sord"); String sortcol = getPara ("SIDX"); vo.setNumPerPage (Größen); vo.setpagenum (currentPage); vo.setorderfield (Sortcol); vo.setOrderDirection (Sorder); return vo; } Konvertieren Sie die von JQGrid übergebenen Parameter in das Basiskonditions -Pagination -Query -Objekt. protected void renderxml (httpServletResponse Res, String xmlresponse) {try {res.setcharactercoding ("utf-8"); res.setheader ("Inhaltstyp", "text/xml"); Printwriter out = res.getWriter (); out.print (xmlresponse); if (out! = null) {out.close (); }} catch (ioException e) {logger.Error (e.getMessage ()); logger.Error (e.getMessage (), e); }}Schreibt XML in den Ausgangsstrom.
Nachdem wir diese grundlegenden Objekte definiert haben, werden wir Daten erhalten und übergeben.
@SuppressWarnings ("rawtypes") @RequestMapPing (value = "DatablePayDealOrderSlist") public void datapaydealordersList (httpServletResponse -Antwort) {try {logger.debug ("Get die Bestellung, die ich bezahlt habe"); XstreamComponent xstreamComponent = xstreamComponent.Newinstance (); // List -Parameter baseConditionvo vo = getBaseconditionVOFROTABLE () abrufen; vo.addparams ("Name", getPara ("Name")); logger.debug ("Ich habe Bestellabfrage bezahlt" + Vo); // Das Projekt, das ich liste mydealorders = Dealorderservice.getByIssUid (vo, vo.creferowbounds ()) erstellt habe; Long Count = Dealorderservice.SearchISSUetotAlcount (VO); String xmlResponse = xstreamComponent.topagexml (CreatePageGrid (mydealorders, vo, count.intValue ()); renderxml (Antwort, xmlresponse.replaceall ("__", "_")); } catch (unkategorizedSQLEXception e) {logger.Error (e.getMessage ()); logger.Error (e.getMessage (), e); renderxml (Antwort, Constants.Query_error); } catch (Ausnahme e) {logger.Error (e.getMessage ()); logger.Error (e.getMessage (), e); renderxml (Antwort, Constants.server_error); }}Lassen Sie uns ausführlich erklären:
1. XStreamComponent.newInstance() erstellt ein XML -Stream -Objekt.
2. BaseConditionVO vo = getBaseConditionVOForTable(); Erstellen Sie ein Pagination -Query -Parameterobjekt.
3 vo.addParams("name", getPara("name")); Legen Sie den Wert des Suchfelds in das Abfragebobjekt ein.
dealOrderService.getByIssueUid(vo, vo.createRowBounds()); Die Pagination Query -Methode von MyBatis ist super einfach. Ein Freund in der Gruppe erstellte speziell eine Paginationskomponente von MyBatis. Ich denke, die Verwendung der ursprünglichen MyBatis -Abfragemethode ist effizienter. Danach werden wir die entsprechende SQL -Schreibmethode von XML in mybatis schreiben.
5. renderXml(response, xmlResponse.replaceAll("__", "_")); Schreiben Sie Daten in den Ausgangsstrom von JSP.
Schließlich stellen wir vor, wie Sie Paging -Daten über MyBatis erhalten.
mappper.java
Paket com.honzh.biz.database.mapper; import Java.math.bigdecimal; Import Java.util.hashMap; @Suppresswarnings ("Rawtypes") List <SeHmap> getbyISSUEUID (Baseconditionvo Vo, Rowbounds CreateRowbounds);}Die beiden Objekte, die Sie Mapper.xml übergeben möchten, sind Basiskonditions- und Paging Rowbounds. SQL in XML paging automatisch.
mappper.xml
<select id = "getByIssUid" resultType = " orderfield!
Sie können niemals auf Reihenbeschwerden achten, MyBatis wird die Grenze für Sie automatisch einschränken. Der Name der Suchdomäne kann direkt über Mo.Name oder direkt erhalten werden. Orderfield und OrderDirection werden ebenfalls verabschiedet.
Bisher ist die gesamte in JQGrid eingebettete Bootstrap erfolgreich beendet. Ok, mach deinen Tisch großartig!