Wie wir auf der SSRS 2014 dynamische Berichte erstellt haben


Wir haben bereits darüber gesprochen, wie wir einem produzierenden Unternehmen geholfen haben, die Prozesse der Unternehmensschulung und Personalentwicklung zu transformieren. Mitarbeiter des Kunden, die in Papierdokumenten und Excel-Tabellen ertranken, erhielten eine praktische iPad-Anwendung und ein Webportal. Eine der wichtigsten Funktionen dieses Produkts ist die Erstellung dynamischer Berichte, anhand derer Manager die Arbeit der Mitarbeiter „vor Ort“ beurteilen. Dies sind riesige Dokumente mit Dutzenden von Feldern und einer durchschnittlichen Größe von 3000 * 1600 Pixel.

In diesem Artikel werden wir darüber sprechen, wie diese Schönheit basierend auf Microsoft SQL Server Reporting Services bereitgestellt wird, warum ein solches Backend schlechte Freunde des Webportals sein kann und welche Tricks helfen, ihre Beziehung aufzubauen. Der gesamte geschäftliche Teil der Lösung wurde bereits im vorherigen Artikel beschrieben, daher konzentrieren wir uns hier auf technische Probleme. Lass uns anfangen!


Formulierung des Problems


Wir haben ein Portal, mit dem mehrere hundert Benutzer arbeiten. Sie sind in einer schrittweisen Hierarchie angeordnet, in der jeder Benutzer einen Supervisor mit einem höheren Rang hat. Diese Differenzierung der Rechte ist erforderlich, damit Benutzer Ereignisse mit untergeordneten Mitarbeitern erstellen können. Sie können über Stufen springen, d.h. Der Benutzer kann die Aktivität mit einem Mitarbeiter eines niedrigeren Ranges als sich selbst beginnen.

Welche Ereignisse sind hier gemeint? Dies kann eine Schulung, Unterstützung oder Zertifizierung eines Mitarbeiters eines Handelsunternehmens sein, die der Vorgesetzte an einer Verkaufsstelle durchführt. Das Ergebnis einer solchen Veranstaltung ist ein Fragebogen, der auf einem iPad mit Mitarbeiterbewertungen für berufliche Qualitäten und Fähigkeiten ausgefüllt wird.

Anhand der Fragebogendaten können Sie Statistiken erstellen, zum Beispiel:

  • Wie viele Ereignisse mit seinen Untergebenen dieser Art hat Vasya Ivanov in einem Monat geschaffen? Wie viele von ihnen sind abgeschlossen?
  • Wie hoch ist der Prozentsatz zufriedenstellender Bewertungen? Welche Fragen beantworten Merchandiser am schlechtesten? Welcher Manager kann schlechter Tests machen?

Solche Statistiken sind in Berichten enthalten , die über die Weboberfläche in den Formaten XLS, PDF, DOCX erstellt und gedruckt werden können. Alle diese Funktionen sind für Manager auf verschiedenen Ebenen konzipiert.

Inhalt und Design der Berichte werden in den Vorlagen definiert , sodass Sie die erforderlichen Parameter festlegen können. Wenn Benutzer in Zukunft neue Arten von Berichten benötigen, kann das System Vorlagen erstellen, veränderbare Parameter angeben und dem Portal eine Vorlage hinzufügen. All dies - ohne den Quellcode und die Arbeitsprozesse des Produkts zu beeinträchtigen.

Spezifikationen und Einschränkungen


Das Portal läuft auf einer Microservice-Architektur, die Vorderseite ist in Angular 5 geschrieben. Die Ressource verwendet die JWT-Autorisierung und unterstützt die Browser Google Chrome, Firefox, Microsoft Edge und IE 11 .

Alle Daten werden auf MS SQL Server 2014 gespeichert. SQL Server Reporting Services (SSRS) ist auf dem Server installiert, der Kunde verwendet sie und wird sie nicht ablehnen. Daher die wichtigste Einschränkung: Der Zugriff auf SSRS ist von außen geschlossen, sodass Sie nur über die NTLM-Autorisierung vom lokalen Netzwerk aus auf die Webschnittstelle und SOAP zugreifen können.

Ein paar Worte zu SSRS
SSRS – , , . docs.microsoft.com, SSRS (API) ( Report Server, - HTTP).

Achtung, die Frage: Wie kann die Aufgabe ohne manuelle Methoden, mit minimalen Ressourcen und maximalem Nutzen für den Kunden erledigt werden?

Da der Kunde SSRS auf einem dedizierten Server hat, lassen Sie SSRS die ganze Drecksarbeit beim Generieren und Exportieren von Berichten erledigen. Dann müssen wir keinen eigenen Berichtsservice schreiben, Module in XLS, PDF, DOCX, HTML und die entsprechende API exportieren.

Daher bestand die Aufgabe darin, SSRS mit dem Portal anzufreunden und den Betrieb der in der Aufgabe angegebenen Funktionen sicherzustellen. Lassen Sie uns also die Liste dieser Szenarien durchgehen - in fast jedem Punkt wurden interessante Feinheiten gefunden.

Lösungsstruktur


Da wir bereits über SSRS verfügen, gibt es alle Tools zum Verwalten von Berichtsvorlagen:

  • Berichtsserver - verantwortlich für die gesamte Logik der Arbeit mit Berichten, deren Speicherung, Generierung, Verwaltung und vielem mehr.
  • Berichts-Manager - ein Dienst mit einer Weboberfläche zum Verwalten von Berichten. Hier können Sie in SQL Server Data Tools erstellte Vorlagen auf den Server hochladen, Zugriffsrechte, Datenquellen und Parameter konfigurieren (einschließlich solcher, die beim Melden von Anforderungen geändert werden können). Er kann Berichte zu heruntergeladenen Vorlagen erstellen und diese in verschiedene Formate hochladen, darunter XLS, PDF, DOCX und HTML.

Insgesamt: Wir erstellen Vorlagen in SQL Server-Datentools. Mit Hilfe des Berichts-Managers füllen wir sie auf dem Berichtsserver aus, konfigurieren sie - und sie ist fertig. Wir können Berichte erstellen und deren Parameter ändern.

Die nächste Frage: Wie kann die Erstellung von Berichten zu bestimmten Vorlagen über das Portal angefordert und das Ergebnis zur Ausgabe an die Benutzeroberfläche oder zum Herunterladen im gewünschten Format nach vorne gebracht werden?

Berichterstattung von SSRS an das Portal


Wie oben erwähnt, verfügt SSRS über eine eigene API für den Zugriff auf Berichte. Wir wollen seine Funktionen jedoch nicht aus Sicherheits- und digitalen Hygienegründen herausgeben - wir müssen nur Daten von SSRS in der richtigen Form anfordern und das Ergebnis an den Benutzer übermitteln. Die Berichtsverwaltung wird von speziell geschultem Kundenpersonal durchgeführt.

Da der Zugriff auf SSRS nur über das lokale Netzwerk erfolgt, erfolgt der Datenaustausch zwischen dem Server und dem Portal über einen Proxy-Dienst.


Datenaustausch zwischen dem Portal und dem Server Mal

sehen, wie es funktioniert und warum ReportProxy hier ist.

Auf der Portalseite haben wir also einen ReportService, auf den das Portal für Berichte zugreift. Der Dienst prüft die Berechtigung des Nutzers, die Höhe seiner Rechte, konvertiert Daten von SSRS in die gewünschte Form im Rahmen des Vertrages.

Die ReportService-API enthält nur zwei Methoden, die für uns völlig ausreichend sind:

  1. GetReports - Gibt die Kennungen und Namen aller Vorlagen an, die der aktuelle Benutzer erhalten kann.
  2. GetReportData (Format, Parameter) - liefert vorgefertigte, exportierte Berichtsdaten im angegebenen Format mit einem bestimmten Parametersatz.

Jetzt benötigen Sie diese beiden Methoden, um mit SSRS kommunizieren und die erforderlichen Daten in der richtigen Form daraus entnehmen zu können. Aus der Dokumentation ist bekannt, dass wir über die SOAP-API über HTTP auf den Berichtsserver zugreifen können. Es scheint, dass sich das Rätsel entwickelt ... Aber tatsächlich erwartet uns hier eine Überraschung.

Da SSRS für die Außenwelt geschlossen ist und Sie es nur über die NTLM-Authentifizierung erreichen können, ist es nicht direkt über das SOAP-Portal verfügbar. Es gibt auch unsere eigenen Wünsche:

  • Geben Sie nur Zugriff auf die erforderlichen Funktionen und verbieten Sie die Änderung sogar.
  • Wenn Sie zu einem anderen Berichtssystem wechseln müssen, sollten die Änderungen im ReportService minimal sein und besser gar nicht erforderlich sein.

Hier hilft uns ReportProxy, das sich auf demselben Computer wie SSRS befindet und für die Weiterleitung von Anforderungen von ReportService an SSRS verantwortlich ist. Die Anforderungsverarbeitung ist wie folgt:

  1. Der Dienst erhält eine Anforderung von ReportService und überprüft die JWT-Autorisierung.
  2. In Übereinstimmung mit der API-Methode durchläuft der Proxy das SOAP-Protokoll in SSRS für die erforderlichen Daten und meldet sich dabei über NTLM an.
  3. Von SSRS empfangene Daten werden als Antwort auf die Anforderung an ReportService zurückgesendet.

Tatsächlich ist ReportProxy ein Adapter zwischen SSRS und ReportService.
Die Steuerung ist wie folgt:
[BasicAuthentication]
public class ReportProxyController : ApiController
{
    [HttpGet()]
    public List<ReportItem> Get(string rootPath)
    {
        //  ...
    }

    public HttpResponseMessage Post([FromBody]ReportRequest request)
    {
        //  ...
    }
}

BasicAuthentication :

public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        var authHeader = actionContext.Request.Headers.Authorization;

        if (authHeader != null)
        {
            var authenticationToken = actionContext.Request.Headers.Authorization.Parameter;
            var tokenFromBase64 = Convert.FromBase64String(authenticationToken);
            var decodedAuthenticationToken = Encoding.UTF8.GetString(tokenFromBase64);
            var usernamePasswordArray = decodedAuthenticationToken.Split(':');
            var userName = usernamePasswordArray[0];
            var password = usernamePasswordArray[1];

            var isValid = userName == BasiAuthConf.Login && password == BasiAuthConf.Password;

            if (isValid)
            {
                var principal = new GenericPrincipal(new GenericIdentity(userName), null);
                Thread.CurrentPrincipal = principal;

                return;
            }
        }

        HandleUnathorized(actionContext);
    }

    private static void HandleUnathorized(HttpActionContext actionContext)
    {
        actionContext.Response = actionContext.Request.CreateResponse(
            HttpStatusCode.Unauthorized
        );

        actionContext.Response.Headers.Add(
            "WWW-Authenticate", "Basic Scheme='Data' location = 'http://localhost:"
        );
    }
}


Infolgedessen sieht der Prozess folgendermaßen aus:

  1. Die Front sendet eine http-Anfrage an ReportService.
  2. ReportService sendet eine http-Anfrage an ReportProxy.
  3. ReportProxy empfängt über die SOAP-Schnittstelle Daten von SSRS und sendet das Ergebnis an ReportService.
  4. ReportService bringt das Ergebnis vertragsgemäß und gibt es an den Kunden weiter.

Wir haben ein funktionierendes System, das eine Liste der verfügbaren Vorlagen anfordert, Berichte an SSRS sendet und sie in allen unterstützten Formaten an die Front weiterleitet. Jetzt müssen Sie die generierten Berichte gemäß den angegebenen Parametern auf der Vorderseite anzeigen, die Möglichkeit geben, sie in XLS-, PDF-, DOCX-Dateien hochzuladen und zu drucken. Beginnen wir mit der Anzeige.

Arbeiten mit SSRS-Berichten im Portal


Auf den ersten Blick ist das eine alltägliche Angelegenheit - der Bericht ist im HTML-Format erhältlich, sodass wir damit machen können, was wir wollen! Wir werden es in die Seite einbetten, es mit Designstilen tönen und das Ding ist im Hut. Tatsächlich stellte sich heraus, dass es genügend Fallstricke gab.

Gemäß dem Entwurfskonzept sollte der Abschnitt der Berichte auf dem Portal aus zwei Seiten bestehen:

1) einer Liste von Vorlagen, wo wir können:

  • Anzeigen von Statistiken zu Aktivitäten für das gesamte Portal;
  • Alle uns zur Verfügung stehenden Vorlagen anzeigen.
  • Klicken Sie auf die gewünschte Vorlage und gehen Sie zum entsprechenden Berichtsgenerator.



2) ein Berichtsgenerator, mit dem wir:

  • Legen Sie Vorlagenparameter fest und erstellen Sie einen Bericht darüber.
  • Sehen Sie sich an, was als Ergebnis passiert ist.
  • Wählen Sie das Ausgabedateiformat aus und laden Sie es herunter.
  • Drucken Sie den Bericht in einer praktischen und visuellen Form.



Es gab keine besonderen Probleme mit der ersten Seite, daher werden wir nicht weiter darauf eingehen. Und der Berichtsgenerator zwang uns, den Ingenieur einzuschalten, damit echte Menschen alle Funktionen von TK bequem nutzen können.

Problem Nummer 1. Riesentische


Entsprechend dem Designkonzept sollte diese Seite einen Anzeigebereich haben, damit der Benutzer seinen Bericht vor dem Export sehen kann. Wenn der Bericht nicht in das Fenster passt, können Sie horizontal und vertikal scrollen. Gleichzeitig kann ein typischer Bericht die Größe mehrerer Bildschirme erreichen, was bedeutet, dass Blöcke mit den Namen von Zeilen und Spalten geklebt werden müssen. Ohne dies müssen Benutzer ständig an den Anfang der Tabelle zurückkehren, um sich zu merken, was eine bestimmte Zelle bedeutet. Im Allgemeinen ist es einfacher, einen Bericht auszudrucken und ständig die erforderlichen Blätter vor den Augen zu halten, aber dann verliert die Tabelle auf dem Bildschirm einfach ihre Bedeutung.

Im Allgemeinen können Klebeblöcke nicht vermieden werden. Und SSRS 2014 kann keine Zeilen und Spalten in einem MHTML-Dokument reparieren - nur in seiner eigenen Weboberfläche.

Hier erinnern wir uns, dass moderne Browser die CSS-Sticky-Eigenschaft unterstützen , die nur die Funktion bietet, die wir benötigen. Wir setzen die Position: klebrig auf den markierten Block, geben den Einzug links oder oben an (links, oben), und der Block bleibt während des horizontalen und vertikalen Bildlaufs an Ort und Stelle.

Sie müssen einen Parameter finden, den CSS abrufen kann. Benutzerdefinierte Zellenwerte, mit denen SSRS 2014 sie in der Weboberfläche erfassen kann, gehen beim Export in HTML verloren. OK, wir werden sie selbst markieren - wir würden nur verstehen, wie.

Nach mehreren Stunden Lesen der Dokumentation und Diskussionen mit Kollegen schien es keine Optionen zu geben. Und hier wurde nach allen Gesetzen des Diagramms das ToolTip-Feld für uns angezeigt, mit dem wir Tooltips für Zellen angeben können. Es stellte sich heraus, dass es in den exportierten HTML-Code im Tooltip-Attribut geworfen wird - genau auf dem Tag, das zur benutzerdefinierten Zelle in SQL Server Data Tools gehört. Es gab keine Wahl - wir haben keinen anderen Weg gefunden, um Zellen für die Fixierung zu markieren.

Sie müssen also Markierungsregeln erstellen und Markierungen in HTML über ToolTip weiterleiten. Anschließend ändern wir mit JS das Tooltip-Attribut an der angegebenen Markierung in die CSS-Klasse.

Es gibt nur zwei Möglichkeiten, Zellen zu fixieren: vertikal (feste Spalte) und horizontal (feste Zeile). Es ist sinnvoll, einen weiteren Marker auf die Eckzellen zu setzen, die beim Scrollen in beide Richtungen an Ort und Stelle bleiben - fest-beide.

Der nächste Schritt ist die Benutzeroberfläche. Wenn Sie ein HTML-Dokument erhalten, müssen Sie alle HTML-Elemente mit Markierungen finden, die Werte erkennen, die entsprechende CSS-Klasse festlegen und das Tooltip-Attribut entfernen, damit es beim Bewegen des Mauszeigers nicht angezeigt wird. Es ist zu beachten, dass das resultierende Markup aus verschachtelten Tabellen (Tabellen-Tags) besteht.

Code anzeigen
type FixationType = 'row' | 'column' | 'both';

init(reportHTML: HTMLElement) {
    //    

    // -  
    const rowsFixed: NodeList = reportHTML.querySelectorAll('[title^="RowFixed"]');
    // -  
    const columnFixed: NodeList = reportHTML.querySelectorAll('[title^="ColumnFixed"]');
    // -    
    const bothFixed: NodeList = reportHTML.querySelectorAll('[title^="BothFixed"]');

    this.prepare(rowsFixed, 'row');
    this.prepare(columnFixed, 'column');
    this.prepare(bothFixed, 'both');
}

//    
prepare(nodeList: NodeList, fixingType: FixationType) {
    for (let i = 0; i < nodeList.length; i++) {
        const element: HTMLElement = nodeList[i];
        //   -
        element.classList.add(fixingType + '-fixed');

        element.removeAttribute('title');
        element.removeAttribute('alt'); //   SSRS

        element.parentElement.classList.add(fixingType  + '-fixed-parent');

        //     ,     
        element.style.width = element.getBoundingClientRect().width  + 'px';
        //     ,     
        element.style.height = element.getBoundingClientRect().height  + 'px';

        //  
        this.calculateCellCascadeParams(element, fixingType);
    }
}


Und hier ist ein neues Problem: Wenn beim Kaskadenverhalten mehrere Blöcke, die sich in eine Richtung bewegen, gleichzeitig in der Tabelle fixiert sind, werden die nacheinander verlaufenden Zellen geschichtet. Gleichzeitig ist nicht klar, wie viel sich jeder nächste Block zurückziehen soll - die Einrückungen müssen über JavaScript basierend auf der Höhe des Blocks davor berechnet werden. All dies gilt sowohl für vertikale als auch für horizontale Anker.

Das Korrektur-Skript hat das Problem gelöst.
//      
calculateCellCascadeParams(cell: HTMLElement, fixationType: FixationType) {
    const currentTD: HTMLTableCellElement = cell.parentElement;
    const currentCellIndex = currentTD.cellIndex;

    //   
    currentTD.style.left = '';
    currentTD.style.top = '';

    const currentTDStyles = getComputedStyle(currentTD);

    //  
    if (fixationType === 'row' || fixationType === 'both') {
        const parentRow: HTMLTableRowElement = currentTD.parentElement;

        //        
        //    .
        //   ,    .
        let previousRow: HTMLTableRowElement = parentRow;
        let topOffset = 0;

        while (previousRow = previousRow.previousElementSibling) {
            let previousCellIndex = 0;
            let cellIndexBulk = 0;

            for (let i = 0; i < previousRow.cells.length; i++) {
                if (previousRow.cells[i].colSpan > 1) {
                    cellIndexBulk += previousRow.cells[i].colSpan;
                } else {
                    cellIndexBulk += 1;
                }

                if ((cellIndexBulk - 1) >= currentCellIndex) {
                    previousCellIndex = i;
                    break;
                }
            }

            const previousCell = previousRow.cells[previousCellIndex];

            if (previousCell.classList.contains(fixationType + '_fixed_parent')) {
                topOffset += previousCell.getBoundingClientRect().height;
            }
        }

        if (topOffset > 0) {
            if (currentTDStyles.top) {
                topOffset += <any>currentTDStyles.top.replace('px', '') - 0;
            }

            currentTD.style.top = topOffset + 'px';
        }
    }

    //  
    if (fixationType === 'column' || fixationType === 'both') {
        //       
        //     .
        //   ,    .
        let previousCell: HTMLTableCellElement = currentTD;
        let leftOffset = 0;

        while (previousCell = previousCell.previousElementSibling) {
            if (previousCell.classList.contains(fixationType + '_fixed_parent')) {
                leftOffset += previousCell.getBoundingClientRect().width;
            }
        }

        if (leftOffset > 0) {
            if (currentTDStyles.left) {
                leftOffset += <any>currentTDStyles.left.replace('px', '') - 0;
            }

            currentTD.style.left = leftOffset + 'px';
        }
    }
}


Der Code überprüft die Tags markierter Elemente und fügt die Parameter der festen Zellen zum Einrückungswert hinzu. Bei anhaftenden Zeilen wird ihre Höhe für Spalten ihre Breite addiert.


Ein Beispiel für einen Bericht mit einer klebrigen oberen Zeile.

Daher sieht der Prozess folgendermaßen aus:

  1. Wir erhalten das Markup von SSRS und fügen es an der richtigen Stelle im DOM ein.
  2. Marker erkennen;
  3. Passen Sie die Parameter für das Kaskadenverhalten an.

Da das Haftverhalten vollständig über CSS implementiert wird und JS nur an der Vorbereitung des eingehenden Dokuments beteiligt ist, funktioniert die Lösung schnell genug und ohne Verzögerungen.

Leider mussten für IE IE-Blöcke deaktiviert werden, weil es unterstützt nicht die Position: klebrige Eigenschaft. Der Rest - Safari, Mozilla Firefox und Chrome - leistet hervorragende Arbeit.

Mach weiter.

Problem Nummer 2. Bericht exportieren


Um einen Bericht aus dem System zu ziehen, müssen Sie (1) über ReportService für ein Blob-Objekt auf das SSRS zugreifen, (2) über die Schnittstelle mithilfe der window.URL.createObjectURL-Methode einen Link zum Objekt erhalten, (3) den Link in das Tag einfügen und einen Klick für simulieren Datei-Upload.

Dies funktioniert in Firefox, Safari und in allen Versionen von Chrome außer Apple. Damit IE, Edge und Chrome für iOS die Funktion ebenfalls unterstützten, musste ich mein Gehirn zurückwerfen.

In IE und Edge löst das Ereignis einfach keine Browseranforderung zum Herunterladen der Datei aus. Diese Browser verfügen über eine solche Funktion, dass zur Simulation eines Klicks eine Bestätigung des Benutzers zum Herunterladen sowie eine eindeutige Angabe weiterer Aktionen erforderlich ist. Die Lösung wurde in der window.navigator.msSaveOrOpenBlob () -Methode gefunden, die sowohl im IE als auch in Edge verfügbar ist. Er weiß nur, wie er den Benutzer um Erlaubnis für den Vorgang bitten und klären kann, was als nächstes zu tun ist. Wir bestimmen also, ob die window.navigator.msSaveOrOpenBlob-Methode vorhanden ist, und reagieren auf die Situation.

Chrome unter iOS hatte keinen solchen Hack und anstelle eines Berichts wurde nur eine leere Seite angezeigt. Beim Durchstöbern des Webs fanden wir eine ähnliche Geschichte, nach der in iOS 13 dieser Fehler hätte behoben werden müssen. Leider haben wir die Anwendung bereits in den Tagen von iOS 12 geschrieben, sodass wir uns am Ende entschieden haben, keine Zeit mehr zu verschwenden, und einfach die Schaltfläche in Chrome für iOS deaktiviert haben.
Nun dazu, wie der endgültige Exportprozess in die Benutzeroberfläche aussieht. In der Angular-Berichtskomponente befindet sich eine Schaltfläche, mit der eine Reihe von Schritten gestartet wird:

  • Über die Parameter des Ereignisses erhält der Handler die Kennung des Exportformats (z. B. "PDF").
  • Sendet eine Anforderung an ReportService, um ein Blob-Objekt für das angegebene Format zu erhalten.
  • prüft, ob der Browser IE oder Edge ist;
  • Wenn die Antwort von ReportService kommt:
    • Wenn es sich um IE oder Edge handelt, wird window.navigator.msSaveOrOpenBlob (fileStream, fileName) aufgerufen.
    • Andernfalls wird die Methode this.exportDownload (fileStream, fileName) aufgerufen, wobei fileStream der Blob ist, der aus der Anforderung an ReportService erhalten wurde, und fileName der Name der zu speichernden Datei ist. Die Methode erstellt ein verstecktes Tag mit einem Link zu window.URL.createObjectURL (fileStream), simuliert einen Klick und entfernt das Tag.

Nachdem dies geklärt war, blieb das letzte Abenteuer bestehen.

Problem Nummer 3. Ausdrucken


Jetzt können wir den Bericht auf dem Portal sehen und in die Formate XLS, PDF und DOCX exportieren. Der Druck des Dokuments muss noch implementiert werden, um einen genauen mehrseitigen Bericht zu erhalten. Wenn sich herausstellte, dass die Tabelle in Seiten unterteilt war, sollte jede von ihnen Überschriften enthalten - dieselben klebrigen Blöcke, über die wir im vorletzten Abschnitt gesprochen haben.

Am einfachsten ist es, die aktuelle Seite mit dem angezeigten Bericht zu übernehmen, alles Überflüssige mit CSS auszublenden und mit der Methode window.print () zum Drucken zu senden. Diese Methode funktioniert aus mehreren Gründen nicht sofort:

  1. Nicht standardmäßiger Anzeigebereich - Der Bericht selbst befindet sich in einem separat scrollbaren Bereich, sodass sich die Seite nicht auf unglaubliche horizontale Abmessungen erstreckt. Durch die Verwendung von window.print () werden Inhalte abgeschnitten, die nicht auf den Bildschirm passen.
  2. , ;
  3. , .

All dies kann mit JS und CSS behoben werden. Wir haben uns jedoch entschlossen, Entwicklern Zeit zu sparen und nach einer Alternative zu window.print () zu suchen.

SSRS kann uns sofort ein fertiges PDF mit einer vorzeigbaren Paginierung geben. Dies erspart uns alle Schwierigkeiten der vorherigen Version. Die einzige Frage ist, ob wir das PDF über einen Browser drucken können.

Da PDF ein Standard von Drittanbietern ist, unterstützen ihn Browser über verschiedene Viewer-Plugins. Kein Plug-In - keine Cartoons, also brauchen wir wieder eine alternative Option.

Und wenn Sie das PDF als Bild auf die Seite legen und diese Seite zum Drucken senden? Es gibt bereits Bibliotheken und Komponenten für Angular, die ein solches Rendering bereitstellen. Gesucht, experimentiert, implementiert.

Um nicht mit den Daten umzugehen, die wir nicht drucken möchten, wurde beschlossen, den gerenderten Inhalt auf eine neue Seite zu übertragen und dort bereits window.print () auszuführen. Infolgedessen ist der gesamte Prozess wie folgt:

  1. Fordern Sie ReportService an, den Bericht im PDF-Format zu exportieren.
  2. Wir erhalten das Blob-Objekt, konvertieren es in eine URL (URL.createObjectURL (fileStream)) und geben die URL dem PDF-Viewer zum Rendern.
  3. Wir nehmen Bilder aus dem PDF-Viewer auf.
  4. Öffnen Sie eine neue Seite und fügen Sie dort ein kleines Markup hinzu (Titel, ein kleiner Einzug).
  5. Fügen Sie das Bild aus dem PDF-Viewer zum Markup hinzu und rufen Sie window.print () auf.

Nach mehreren Überprüfungen wurde auch ein JS-Code auf der Seite angezeigt, der vor dem Drucken überprüft, ob alle Bilder geladen wurden.

Somit wird das gesamte Erscheinungsbild des Dokuments durch die Parameter der SSRS-Vorlage bestimmt, und die Benutzeroberfläche stört diesen Prozess nicht. Dies reduziert die Anzahl möglicher Fehler. Da Bilder zum Drucken übertragen werden, sind wir gegen Beschädigungen oder Verformungen des Layouts versichert.

Es gibt auch Nachteile:

  • Ein umfangreicher Bericht wird viel wiegen, was sich nachteilig auf mobile Plattformen auswirken wird.
  • Das Design wird nicht automatisch aktualisiert. Farben, Schriftarten und andere Designelemente müssen auf Vorlagenebene installiert werden.

In unserem Fall wurde das häufige Hinzufügen neuer Vorlagen nicht erwartet, sodass die Lösung akzeptabel war. Mobile Leistung ist selbstverständlich.

Das letzte Wort


So lässt uns ein reguläres Projekt erneut nach einfachen Lösungen für nicht triviale Aufgaben suchen. Das Endprodukt erfüllt die Designanforderungen vollständig und sieht wunderschön aus. Und was am wichtigsten ist: Obwohl wir nicht nach den offensichtlichsten Implementierungsmethoden suchen mussten, wurde die Aufgabe schneller erledigt, als wenn wir das ursprüngliche Berichtsmodul mit allen Konsequenzen übernommen hätten. Am Ende konnten wir uns auf die Geschäftsziele des Projekts konzentrieren.

Source: https://habr.com/ru/post/undefined/


All Articles