Neuere Versionen von Ext JS, insbesondere das Modern Toolkit, senkten die Schwelle für den Eintritt in das Framework (Beispiele für Kitchen Sink), vereinfachten die Erstellung der gewünschten Benutzeroberfläche (hi Sencha Architect) und erreichten eine Mindestgröße für Webanwendungen (Sencha Cmd).
Vielleicht sollte Habr mit einem Beispiel für die Implementierung eines „Situationszentrums“ verwässert werden, in dem Sie in Echtzeit Kameras und Ereignisse von diesen beobachten können (alle Daten sind gefälscht).

Fangen wir an. Erstellen wir ein Spring Boot-Projekt mit 2 Controllern, das eine Liste der Kameras, vorhandenen Ereignisse sowie die Möglichkeit zum Abonnieren neuer Ereignisse (über WebSocket) enthält.

Fügen Sie als Nächstes die erforderlichen Modelle, Speicher, Ansichten und externen Abhängigkeiten hinzu:

Im Einzelnen interagiert die Kartenansicht mit der React-Komponente der Karte.Ext.define('Cameras.view.override.Map', { override: 'Cameras.view.Map', config: { cameras: {}, react: null }, initialize: function() { let that = this; let e = React.createElement; this.setReact(ReactDOM.render(e(createReactClass({ getInitialState: function() { return { items: [], center: [55.751574, 37.573856]}; }, render: function() { let placemarks = []; for(let i=0; i < this.state.items.length; i++) { let location = this.state.items[i].get("location"); placemarks.push(e(window.ReactYandexMaps.Placemark, { geometry: [location.latitude, location.longitude], options: { preset: 'islands#blueCircleDotIconWithCaption', iconCaptionMaxWidth: '50' } })); } let map = e(window.ReactYandexMaps.Map, { state: { center: this.state.center, zoom: 10 }, width: '100%', height: '100%' }, placemarks); return e(window.ReactYandexMaps.YMaps, null, map); } })), this.mapContainer.dom)); }, getElementConfig: function() { return { reference: 'element', className: 'x-container', children: [{ reference: 'bodyElement', style: 'width: 100%; height: 100%', className: 'x-inner', children: [{ style: 'width: 100%; height: 100%', reference: 'mapContainer', className: Ext.baseCSSPrefix + 'map-container' }] }] }; }, addCamera: function(cameraModel) { if(!this.containsCamera(cameraModel)) { this.getCameras()[cameraModel.get("id")] = cameraModel; this.getReact().setState({ items: Object.values(this.getCameras()) }); this.fitCamera(cameraModel); } }, removeCamera: function(cameraModel) { if(this.containsCamera(cameraModel)) { delete this.getCameras()[cameraModel.get("id")]; this.getReact().setState({ items: Object.values(this.getCameras()) }); } }, fitCamera: function(cameraModel) { if(this.containsCamera(cameraModel)) { let location = this.getCameras()[cameraModel.get("id")].get("location"); this.getReact().setState({ center: [location.latitude, location.longitude] }); } }, privates: { containsCamera: function(cameraModel) { cameraId = "" + cameraModel.get("id"); return Object.keys(this.getCameras()).includes(cameraId); } } });
Wir stimmen auch zu, dass die Ereignisse von der CamerasGrid-Komponente as stammen Es ist diese Komponente, die für das Hinzufügen / Entfernen von Kameras von der Karte verantwortlich ist.
CamerasGrid-Komponentencontroller, der der Komponente die Ereigniserzeugung hinzufügt Ext.define('Cameras.view.CamerasGridViewController', { extend: 'Ext.app.ViewController', alias: 'controller.camerasgrid', init: function() { let socket = new WebSocket("ws://localhost:8080/events/sub"); socket.onopen = function(e) { console.log('onopen'); }; socket.onmessage = this.onMessage.bind(this); }, onMessage: function(event) { let data = Ext.decode(event.data); let gridData = this.getView().getStore().getData(); for(let i=0; i < gridData.length; i++) { let checked = gridData.getAt(i).get("checked"); if(checked !== undefined && checked) { if(gridData.getAt(i).get("id") == data.camera.id) { this.fireViewEvent("cameraRecognition", data); } } } } });
Das Ergebnis ist eine recht unterhaltsame Oberfläche. Ich stelle fest, dass bei ordnungsgemäßem Entwurf der Anwendungsarchitektur (auch einer so kleinen) der Zeitaufwand für die Erstellung einer solchen sehr gering ist, und zwar bis zu einigen Stunden.

Beispielcode ist auf
github.com verfügbar.