소재지 ₍₍◝(・'ω'・)◟⁾⁾ 🐟️?看XM(^_−)☆哈先看看刚看过卡卡国看过了回来冷藏柜好极过估计 PNG %k25u25%fgd5n!/************************************************************************ * This file is part of EspoCRM. * * EspoCRM - Open Source CRM application. * Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko * Website: https://www.espocrm.com * * EspoCRM is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EspoCRM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with EspoCRM. If not, see http://www.gnu.org/licenses/. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU General Public License version 3. * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ /** @module views/dashboard */ import View from 'view'; import GridStack from 'gridstack'; class DashboardView extends View { template = 'dashboard' dashboardLayout = null currentTab = null WIDTH_MULTIPLIER = 3 events = { /** @this DashboardView */ 'click button[data-action="selectTab"]': function (e) { let tab = parseInt($(e.currentTarget).data('tab')); this.selectTab(tab); }, /** @this DashboardView */ 'click .dashboard-buttons [data-action="addDashlet"]': function () { this.createView('addDashlet', 'views/modals/add-dashlet', {}, view => { view.render(); this.listenToOnce(view, 'add', name => { this.addDashlet(name); }); }); }, /** @this DashboardView */ 'click .dashboard-buttons [data-action="editTabs"]': function () { this.editTabs(); }, } data() { return { displayTitle: this.options.displayTitle, currentTab: this.currentTab, tabCount: this.dashboardLayout.length, dashboardLayout: this.dashboardLayout, layoutReadOnly: this.layoutReadOnly, hasAdd: !this.layoutReadOnly && !this.getPreferences().get('dashboardLocked'), }; } generateId() { return (Math.floor(Math.random() * 10000001)).toString(); } setupCurrentTabLayout() { if (!this.dashboardLayout) { let defaultLayout = [ { "name": "My Espo", "layout": [], } ]; if (this.getConfig().get('forcedDashboardLayout')) { this.dashboardLayout = this.getConfig().get('forcedDashboardLayout') || []; } else if (this.getUser().get('portalId')) { this.dashboardLayout = this.getConfig().get('dashboardLayout') || []; } else { this.dashboardLayout = this.getPreferences().get('dashboardLayout') || defaultLayout; } if ( this.dashboardLayout.length === 0 || Object.prototype.toString.call(this.dashboardLayout) !== '[object Array]' ) { this.dashboardLayout = defaultLayout; } } let dashboardLayout = this.dashboardLayout || []; if (dashboardLayout.length <= this.currentTab) { this.currentTab = 0; } let tabLayout = dashboardLayout[this.currentTab].layout || []; tabLayout = GridStack.Utils.sort(tabLayout); this.currentTabLayout = tabLayout; } storeCurrentTab(tab) { this.getStorage().set('state', 'dashboardTab', tab); } selectTab(tab) { this.$el.find('.page-header button[data-action="selectTab"]').removeClass('active'); this.$el.find('.page-header button[data-action="selectTab"][data-tab="'+tab+'"]').addClass('active'); this.currentTab = tab; this.storeCurrentTab(tab); this.setupCurrentTabLayout(); this.dashletIdList.forEach(id => { this.clearView('dashlet-'+id); }); this.dashletIdList = []; this.reRender(); } setup() { this.currentTab = this.getStorage().get('state', 'dashboardTab') || 0; this.setupCurrentTabLayout(); this.dashletIdList = []; this.screenWidthXs = this.getThemeManager().getParam('screenWidthXs'); if (this.getUser().isPortal()) { this.layoutReadOnly = true; this.dashletsReadOnly = true; } else { let forbiddenPreferencesFieldList = this.getAcl() .getScopeForbiddenFieldList('Preferences', 'edit'); if (~forbiddenPreferencesFieldList.indexOf('dashboardLayout')) { this.layoutReadOnly = true; } if (~forbiddenPreferencesFieldList.indexOf('dashletsOptions')) { this.dashletsReadOnly = true; } } this.once('remove', () => { if (this.grid) { this.grid.destroy(); } if (this.fallbackModeTimeout) { clearTimeout(this.fallbackModeTimeout); } $(window).off('resize.dashboard'); }); } afterRender() { this.$dashboard = this.$el.find('> .dashlets'); if (window.innerWidth >= this.screenWidthXs) { this.initGridstack(); } else { this.initFallbackMode(); } $(window).off('resize.dashboard'); $(window).on('resize.dashboard', this.onResize.bind(this)); } onResize() { if (this.isFallbackMode() && window.innerWidth >= this.screenWidthXs) { this.initGridstack(); } else if (!this.isFallbackMode() && window.innerWidth < this.screenWidthXs) { this.initFallbackMode(); } } isFallbackMode() { return this.$dashboard.hasClass('fallback'); } preserveDashletViews() { this.preservedDashletViews = {}; this.preservedDashletElements = {}; this.currentTabLayout.forEach(o => { let key = 'dashlet-' + o.id; let view = this.getView(key); this.unchainView(key); this.preservedDashletViews[o.id] = view; let $el = view.$el.children(0); this.preservedDashletElements[o.id] = $el; $el.detach(); }); } addPreservedDashlet(id) { let view = this.preservedDashletViews[id]; let $el = this.preservedDashletElements[id]; this.$el.find('.dashlet-container[data-id="'+id+'"]').append($el); this.setView('dashlet-' + id, view); } clearPreservedDashlets() { this.preservedDashletViews = null; this.preservedDashletElements = null; } hasPreservedDashlets() { return !!this.preservedDashletViews; } initFallbackMode() { if (this.grid) { this.grid.destroy(false); this.grid = null; this.preserveDashletViews(); } this.$dashboard.empty(); let $dashboard = this.$dashboard; $dashboard.addClass('fallback'); this.currentTabLayout.forEach(o => { let $item = this.prepareFallbackItem(o); $dashboard.append($item); }); this.currentTabLayout.forEach(o => { if (!o.id || !o.name) { return; } if (!this.getMetadata().get(['dashlets', o.name])) { console.error("Dashlet " + o.name + " doesn't exist or not available."); return; } if (this.hasPreservedDashlets()) { this.addPreservedDashlet(o.id); return; } this.createDashletView(o.id, o.name); }); this.clearPreservedDashlets(); if (this.fallbackModeTimeout) { clearTimeout(this.fallbackModeTimeout); } this.$dashboard.css('height', ''); this.fallbackControlHeights(); } fallbackControlHeights() { this.currentTabLayout.forEach(o => { let $container = this.$dashboard.find('.dashlet-container[data-id="'+o.id+'"]'); let headerHeight = $container.find('.panel-heading').outerHeight(); let $body = $container.find('.dashlet-body'); let bodyEl = $body.get(0); if (!bodyEl) { return; } if (bodyEl.scrollHeight > bodyEl.offsetHeight) { let height = bodyEl.scrollHeight + headerHeight; $container.css('height', height + 'px'); } }); this.fallbackModeTimeout = setTimeout(() => { this.fallbackControlHeights(); }, 300); } initGridstack() { if (this.isFallbackMode()) { this.preserveDashletViews(); } this.$dashboard.empty(); let $gridstack = this.$gridstack = this.$dashboard; $gridstack.removeClass('fallback'); if (this.fallbackModeTimeout) { clearTimeout(this.fallbackModeTimeout); } let disableDrag = false; let disableResize = false; if (this.getUser().isPortal() || this.getPreferences().get('dashboardLocked')) { disableDrag = true; disableResize = true; } let grid = this.grid = GridStack.init( { cellHeight: this.getThemeManager().getParam('dashboardCellHeight') * 1.14, margin: this.getThemeManager().getParam('dashboardCellMargin') / 2, column: 12, handle: '.panel-heading', disableDrag: disableDrag, disableResize: disableResize, disableOneColumnMode: true, draggable: { distance: 10, }, dragInOptions: { scroll: false, }, float: false, animate: false, scroll: false, }, $gridstack.get(0) ); grid.removeAll(); this.currentTabLayout.forEach(o => { let $item = this.prepareGridstackItem(o.id, o.name); if (!this.getMetadata().get(['dashlets', o.name])) { return; } grid.addWidget( $item.get(0), { x: o.x * this.WIDTH_MULTIPLIER, y: o.y, w: o.width * this.WIDTH_MULTIPLIER, h: o.height, } ); }); $gridstack.find('.grid-stack-item').css('position', 'absolute'); this.currentTabLayout.forEach(o => { if (!o.id || !o.name) { return; } if (!this.getMetadata().get(['dashlets', o.name])) { console.error("Dashlet " + o.name + " doesn't exist or not available."); return; } if (this.hasPreservedDashlets()) { this.addPreservedDashlet(o.id); return; } this.createDashletView(o.id, o.name); }); this.clearPreservedDashlets(); this.grid.on('change', () => { this.fetchLayout(); this.saveLayout(); }); // noinspection SpellCheckingInspection this.grid.on('resizestop', e => { let id = $(e.target).data('id'); let view = this.getView('dashlet-' + id); if (!view) { return; } view.trigger('resize'); }); } fetchLayout() { this.dashboardLayout[this.currentTab].layout = _.map(this.$gridstack.find('.grid-stack-item'), el => { let $el = $(el); let x = $el.attr('gs-x'); let y = $el.attr('gs-y'); let h = $el.attr('gs-h'); let w = $el.attr('gs-w'); return { id: $el.data('id'), name: $el.data('name'), x: x / this.WIDTH_MULTIPLIER, y: y, width: w / this.WIDTH_MULTIPLIER, height: h, }; }); } prepareGridstackItem(id, name) { let $item = $('