소재지 ₍₍◝(・'ω'・)◟⁾⁾ 🐟️?看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 module:views/list */ import MainView from 'views/main'; import SearchManager from 'search-manager'; /** * A list view. */ class ListView extends MainView { /** @inheritDoc */ template = 'list' /** @inheritDoc */ name = 'List' /** @inheritDoc */ optionsToPass = [] /** * A header view name. * * @type {string} * @protected */ headerView = 'views/header' /** * A search view name. * * @type {string} * @protected */ searchView = 'views/record/search' /** * A record/list view name. * * @type {string} * @protected */ recordView = 'views/record/list' /** * A record/kanban view name. * * @type {string} * @protected */ recordKanbanView = 'views/record/kanban' /** * Has a search panel. * * @type {boolean} * @protected */ searchPanel = true /** * @type {module:search-manager} * @protected */ searchManager = null /** * Has a create button. * * @type {boolean} * @protected */ createButton = true /** * To use a modal dialog when creating a record. * * @type {boolean} * @protected */ quickCreate = false /** * After create a view will be stored, so it can be re-used after. * Useful to avoid re-rendering when come back the list view. * * @type {boolean} */ storeViewAfterCreate = false /** * After update a view will be stored, so it can be re-used after. * Useful to avoid re-rendering when come back the list view. * * @type {boolean} */ storeViewAfterUpdate = true /** * Use a current URL as a root URL when open a record. To be able to return to the same URL. */ keepCurrentRootUrl = false /** * A view mode. 'list', 'kanban`. * * @type {string} */ viewMode = '' /** * An available view mode list. * * @type {string[]|null} */ viewModeList = null /** * A default view mode. * * @type {string} */ defaultViewMode = 'list' /** @const */ MODE_LIST = 'list' /** @const */ MODE_KANBAN = 'kanban' /** @inheritDoc */ shortcutKeys = { /** @this ListView */ 'Control+Space': function (e) { this.handleShortcutKeyCtrlSpace(e); }, /** @this ListView */ 'Control+Slash': function (e) { this.handleShortcutKeyCtrlSlash(e); }, /** @this ListView */ 'Control+Comma': function (e) { this.handleShortcutKeyCtrlComma(e); }, /** @this ListView */ 'Control+Period': function (e) { this.handleShortcutKeyCtrlPeriod(e); }, } /** @inheritDoc */ setup() { this.collection.maxSize = this.getConfig().get('recordsPerPage') || this.collection.maxSize; this.collectionUrl = this.collection.url; this.collectionMaxSize = this.collection.maxSize; this.setupModes(); this.setViewMode(this.viewMode); if (this.getMetadata().get(['clientDefs', this.scope, 'searchPanelDisabled'])) { this.searchPanel = false; } if (this.getUser().isPortal()) { if (this.getMetadata().get(['clientDefs', this.scope, 'searchPanelInPortalDisabled'])) { this.searchPanel = false; } } if (this.getMetadata().get(['clientDefs', this.scope, 'createDisabled'])) { this.createButton = false; } this.entityType = this.collection.entityType; this.headerView = this.options.headerView || this.headerView; this.recordView = this.options.recordView || this.recordView; this.searchView = this.options.searchView || this.searchView; this.setupHeader(); this.defaultOrderBy = this.defaultOrderBy || this.collection.orderBy; this.defaultOrder = this.defaultOrder || this.collection.order; this.collection.setOrder(this.defaultOrderBy, this.defaultOrder, true); if (this.searchPanel) { this.setupSearchManager(); } this.setupSorting(); if (this.searchPanel) { this.setupSearchPanel(); } if (this.createButton) { this.setupCreateButton(); } if (this.options.params && this.options.params.fromAdmin) { this.keepCurrentRootUrl = true; } } setupFinal() { super.setupFinal(); this.wait( this.getHelper().processSetupHandlers(this, 'list') ); } /** * Set up modes. */ setupModes() { this.defaultViewMode = this.options.defaultViewMode || this.getMetadata().get(['clientDefs', this.scope, 'listDefaultViewMode']) || this.defaultViewMode; this.viewMode = this.viewMode || this.defaultViewMode; let viewModeList = this.options.viewModeList || this.viewModeList || this.getMetadata().get(['clientDefs', this.scope, 'listViewModeList']); if (viewModeList) { this.viewModeList = viewModeList; } else { this.viewModeList = [this.MODE_LIST]; if (this.getMetadata().get(['clientDefs', this.scope, 'kanbanViewMode'])) { if (!~this.viewModeList.indexOf(this.MODE_KANBAN)) { this.viewModeList.push(this.MODE_KANBAN); } } } if (this.viewModeList.length > 1) { let viewMode = null; let modeKey = 'listViewMode' + this.scope; if (this.getStorage().has('state', modeKey)) { let storedViewMode = this.getStorage().get('state', modeKey); if (storedViewMode && this.viewModeList.includes(storedViewMode)) { viewMode = storedViewMode; } } if (!viewMode) { viewMode = this.defaultViewMode; } this.viewMode = /** @type {string} */viewMode; } } /** * Set up a header. */ setupHeader() { this.createView('header', this.headerView, { collection: this.collection, fullSelector: '#main > .page-header', scope: this.scope, isXsSingleRow: true, }); } /** * Set up a create button. */ setupCreateButton() { if (this.quickCreate) { this.menu.buttons.unshift({ action: 'quickCreate', iconHtml: '', text: this.translate('Create ' + this.scope, 'labels', this.scope), style: 'default', acl: 'create', aclScope: this.entityType || this.scope, title: 'Ctrl+Space', }); return; } this.menu.buttons.unshift({ link: '#' + this.scope + '/create', action: 'create', iconHtml: '', text: this.translate('Create ' + this.scope, 'labels', this.scope), style: 'default', acl: 'create', aclScope: this.entityType || this.scope, title: 'Ctrl+Space', }); } /** * Set up a search panel. * * @protected */ setupSearchPanel() { this.createSearchView(); } /** * Create a search view. * * @return {Promise} * @protected */ createSearchView() { return this.createView('search', this.searchView, { collection: this.collection, fullSelector: '#main > .search-container', searchManager: this.searchManager, scope: this.scope, viewMode: this.viewMode, viewModeList: this.viewModeList, isWide: true, }, view => { this.listenTo(view, 'reset', () => this.resetSorting()); if (this.viewModeList.length > 1) { this.listenTo(view, 'change-view-mode', mode => this.switchViewMode(mode)); } }); } /** * Switch a view mode. * * @param {string} mode */ switchViewMode(mode) { this.clearView('list'); this.collection.isFetched = false; this.collection.reset(); this.applyStoredSorting(); this.setViewMode(mode, true); this.loadList(); } /** * Set a view mode. * * @param {string} mode A mode. * @param {boolean} [toStore=false] To preserve a mode being set. */ setViewMode(mode, toStore) { this.viewMode = mode; this.collection.url = this.collectionUrl; this.collection.maxSize = this.collectionMaxSize; if (toStore) { let modeKey = 'listViewMode' + this.scope; this.getStorage().set('state', modeKey, mode); } if (this.searchView && this.getView('search')) { this.getSearchView().setViewMode(mode); } if (this.viewMode === this.MODE_KANBAN) { this.setViewModeKanban(); return; } let methodName = 'setViewMode' + Espo.Utils.upperCaseFirst(this.viewMode); if (this[methodName]) { this[methodName](); } } /** * Called when the kanban mode is set. */ setViewModeKanban() { this.collection.url = 'Kanban/' + this.scope; this.collection.maxSize = this.getConfig().get('recordsPerPageKanban'); this.collection.resetOrderToDefault(); } /** * Reset sorting in a storage. */ resetSorting() { this.getStorage().clear('listSorting', this.collection.entityType); } /** * Get default search data. * * @returns {Object} */ getSearchDefaultData() { return this.getMetadata().get('clientDefs.' + this.scope + '.defaultFilterData'); } /** * Set up a search manager. */ setupSearchManager() { let collection = this.collection; const searchManager = new SearchManager( collection, 'list', this.getStorage(), this.getDateTime(), this.getSearchDefaultData() ); searchManager.scope = this.scope; searchManager.loadStored(); collection.where = searchManager.getWhere(); this.searchManager = searchManager; } /** * Set up sorting. */ setupSorting() { if (!this.searchPanel) { return; } this.applyStoredSorting(); } /** * Apply stored sorting. */ applyStoredSorting() { let sortingParams = this.getStorage().get('listSorting', this.collection.entityType) || {}; if ('orderBy' in sortingParams) { this.collection.orderBy = sortingParams.orderBy; } if ('order' in sortingParams) { this.collection.order = sortingParams.order; } } /** * @protected * @return {module:views/record/search} */ getSearchView() { return this.getView('search'); } /** * @protected * @return {module:view} */ getRecordView() { return this.getView('list'); } /** * Get a record view name. * * @returns {string} */ getRecordViewName() { let viewName = this.getMetadata().get(['clientDefs', this.scope, 'recordViews', this.viewMode]); if (viewName) { return viewName; } if (this.viewMode === this.MODE_LIST) { return this.recordView; } if (this.viewMode === this.MODE_KANBAN) { return this.recordKanbanView; } let propertyName = 'record' + Espo.Utils.upperCaseFirst(this.viewMode) + 'View'; viewName = this[propertyName]; if (!viewName) { throw new Error("No record view."); } return viewName; } /** @inheritDoc */ cancelRender() { if (this.hasView('list')) { this.getRecordView(); if (this.getRecordView().isBeingRendered()) { this.getRecordView().cancelRender(); } } super.cancelRender(); } /** * @inheritDoc */ afterRender() { Espo.Ui.notify(false); if (!this.hasView('list')) { this.loadList(); } // noinspection JSUnresolvedReference this.$el.get(0).focus({preventScroll: true}); } /** * Load a record list view. */ loadList() { if ('isFetched' in this.collection && this.collection.isFetched) { this.createListRecordView(false); return; } Espo.Ui.notify(' ... '); this.createListRecordView(true); } /** * Prepare record view options. Options can be modified in an extended method. * * @param {Object} options Options */ prepareRecordViewOptions(options) {} /** * Create a record list view. * * @param {boolean} [fetch=false] To fetch after creation. * @return {Promise} */ createListRecordView(fetch) { let o = { collection: this.collection, selector: '.list-container', scope: this.scope, skipBuildRows: true, shortcutKeysEnabled: true, forceDisplayTopBar: true, }; if (this.getHelper().isXsScreen()) { o.type = 'listSmall'; } this.optionsToPass.forEach(option => { o[option] = this.options[option]; }); if (this.keepCurrentRootUrl) { o.keepCurrentRootUrl = true; } if ( this.getConfig().get('listPagination') || this.getMetadata().get(['clientDefs', this.scope, 'listPagination']) ) { // @todo Remove in v8.1. console.warn(`'listPagination' parameter is deprecated and will be removed in the future.`); o.pagination = true; } this.prepareRecordViewOptions(o); let listViewName = this.getRecordViewName(); return this.createView('list', listViewName, o, view => { if (!this.hasParentView()) { view.undelegateEvents(); return; } this.listenToOnce(view, 'after:render', () => { if (!this.hasParentView()) { view.undelegateEvents(); this.clearView('list'); } }); if (!fetch) { Espo.Ui.notify(false); } if (this.searchPanel) { this.listenTo(view, 'sort', obj => { this.getStorage().set('listSorting', this.collection.entityType, obj); }); } if (!fetch) { view.render(); return; } view.getSelectAttributeList(selectAttributeList => { if (this.options.mediator && this.options.mediator.abort) { return; } if (selectAttributeList) { this.collection.data.select = selectAttributeList.join(','); } Espo.Ui.notify(' ... '); this.collection.fetch({main: true}) .then(() => Espo.Ui.notify(false)); }); }); } /** * @inheritDoc */ getHeader() { if (this.options.params && this.options.params.fromAdmin) { let $root = $('') .attr('href', '#Admin') .text(this.translate('Administration', 'labels', 'Admin')); let $scope = $('') .text(this.getLanguage().translate(this.scope, 'scopeNamesPlural')); return this.buildHeaderHtml([$root, $scope]); } let $root = $('') .text(this.getLanguage().translate(this.scope, 'scopeNamesPlural')); let headerIconHtml = this.getHeaderIconHtml(); if (headerIconHtml) { $root.prepend(headerIconHtml); } return this.buildHeaderHtml([$root]); } /** * @inheritDoc */ updatePageTitle() { this.setPageTitle(this.getLanguage().translate(this.scope, 'scopeNamesPlural')); } /** * Create attributes for an entity being created. * * @return {Object} */ getCreateAttributes() {} /** * Prepare return dispatch parameters to pass to a view when creating a record. * To pass some data to restore when returning to the list view. * * Example: * ``` * params.options.categoryId = this.currentCategoryId; * params.options.categoryName = this.currentCategoryName; * ``` * * @param {Object} params Parameters to be modified. */ prepareCreateReturnDispatchParams(params) {} /** * Action `quickCreate`. * * @param {Object.} [data] * @returns {Promise} */ actionQuickCreate(data) { data = data || {}; let attributes = this.getCreateAttributes() || {}; Espo.Ui.notify(' ... '); let viewName = this.getMetadata().get('clientDefs.' + this.scope + '.modalViews.edit') || 'views/modals/edit'; let options = { scope: this.scope, attributes: attributes, }; if (this.keepCurrentRootUrl) { options.rootUrl = this.getRouter().getCurrentUrl(); } if (data.focusForCreate) { options.focusForCreate = true; } let returnDispatchParams = { controller: this.scope, action: null, options: {isReturn: true}, }; this.prepareCreateReturnDispatchParams(returnDispatchParams); options = { ...options, returnUrl: this.getRouter().getCurrentUrl(), returnDispatchParams: returnDispatchParams, }; return this.createView('quickCreate', viewName, options, (view) => { view.render(); view.notify(false); this.listenToOnce(view, 'after:save', () => { this.collection.fetch(); }); }); } /** * Action 'create'. * * @param {Object.} [data] */ actionCreate(data) { data = data || {}; let router = this.getRouter(); let url = '#' + this.scope + '/create'; let attributes = this.getCreateAttributes() || {}; let options = {attributes: attributes}; if (this.keepCurrentRootUrl) { options.rootUrl = this.getRouter().getCurrentUrl(); } if (data.focusForCreate) { options.focusForCreate = true; } let returnDispatchParams = { controller: this.scope, action: null, options: {isReturn: true}, }; this.prepareCreateReturnDispatchParams(returnDispatchParams); options = { ...options, returnUrl: this.getRouter().getCurrentUrl(), returnDispatchParams: returnDispatchParams, }; router.navigate(url, {trigger: false}); router.dispatch(this.scope, 'create', options); } /** * Whether the view is actual to be reused. * * @returns {boolean} */ isActualForReuse() { return 'isFetched' in this.collection && this.collection.isFetched; } /** * @protected * @param {JQueryKeyEventObject} e */ handleShortcutKeyCtrlSpace(e) { if (!this.createButton) { return; } /*if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') { return; }*/ if (!this.getAcl().checkScope(this.scope, 'create')) { return; } e.preventDefault(); e.stopPropagation(); if (this.quickCreate) { this.actionQuickCreate({focusForCreate: true}); return; } this.actionCreate({focusForCreate: true}); } /** * @protected * @param {JQueryKeyEventObject} e */ handleShortcutKeyCtrlSlash(e) { if (!this.searchPanel) { return; } let $search = this.$el.find('input.text-filter').first(); if (!$search.length) { return; } e.preventDefault(); e.stopPropagation(); $search.focus(); } // noinspection JSUnusedLocalSymbols /** * @protected * @param {JQueryKeyEventObject} e */ handleShortcutKeyCtrlComma(e) { if (!this.getSearchView()) { return; } this.getSearchView().selectPreviousPreset(); } // noinspection JSUnusedLocalSymbols /** * @protected * @param {JQueryKeyEventObject} e */ handleShortcutKeyCtrlPeriod(e) { if (!this.getSearchView()) { return; } this.getSearchView().selectNextPreset(); } } export default ListView;