소재지 ₍₍◝(・'ω'・)◟⁾⁾ 🐟️?看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/detail */ import MainView from 'views/main'; /** * A detail view. */ class DetailView extends MainView { /** @inheritDoc */ template = 'detail' /** @inheritDoc */ name = 'Detail' /** @inheritDoc */ optionsToPass = [ 'attributes', 'returnUrl', 'returnDispatchParams', 'rootUrl', ] /** * A header view name. * * @type {string} */ headerView = 'views/header' /** * A record view name. * * @type {string} */ recordView = 'views/record/detail' /** * A root breadcrumb item not to be a link. * * @type {boolean} */ rootLinkDisabled = false /** * A root URL. * * @type {string} */ rootUrl = '' /** * Is return. * * @protected */ isReturn = false /** @inheritDoc */ shortcutKeys = {} /** * An entity type. * * @type {string} */ entityType /** @inheritDoc */ setup() { super.setup(); this.entityType = this.model.entityType || this.model.name; this.headerView = this.options.headerView || this.headerView; this.recordView = this.options.recordView || this.recordView; this.rootUrl = this.options.rootUrl || this.options.params.rootUrl || '#' + this.scope; this.isReturn = this.options.isReturn || this.options.params.isReturn || false; this.setupHeader(); this.setupRecord(); this.setupPageTitle(); this.initFollowButtons(); this.initRedirect(); } /** @inheritDoc */ setupFinal() { super.setupFinal(); this.wait( this.getHelper().processSetupHandlers(this, 'detail') ); } /** @private */ initRedirect() { if (!this.options.params.isAfterCreate) { return; } let redirect = () => { Espo.Ui.success(this.translate('Created')); setTimeout(() => { this.getRouter().navigate(this.rootUrl, {trigger: true}); }, 1000) }; if ( this.model.lastSyncPromise && this.model.lastSyncPromise.getStatus() === 403 ) { redirect(); return; } this.listenToOnce(this.model, 'fetch-forbidden', () => redirect()) } /** * Set up a page title. */ setupPageTitle() { this.listenTo(this.model, 'after:save', () => { this.updatePageTitle(); }); this.listenTo(this.model, 'sync', (model) => { if (model && model.hasChanged('name')) { this.updatePageTitle(); } }); } /** * Set up a header. */ setupHeader() { this.createView('header', this.headerView, { model: this.model, fullSelector: '#main > .header', scope: this.scope, fontSizeFlexible: true, }); this.listenTo(this.model, 'sync', (model) => { if (model && model.hasChanged('name')) { if (this.getView('header')) { this.getView('header').reRender(); } } }); } /** * Set up a record. */ setupRecord() { let o = { model: this.model, fullSelector: '#main > .record', scope: this.scope, shortcutKeysEnabled: true, isReturn: this.isReturn, }; this.optionsToPass.forEach((option) => { o[option] = this.options[option]; }); if (this.options.params && this.options.params.rootUrl) { o.rootUrl = this.options.params.rootUrl; } if (this.model.get('deleted')) { o.readOnly = true; } return this.createView('record', this.getRecordViewName(), o); } /** * Get a record view name. * * @returns {string} */ getRecordViewName() { return this.getMetadata() .get('clientDefs.' + this.scope + '.recordViews.detail') || this.recordView; } /** @private */ initFollowButtons() { if (!this.getMetadata().get(['scopes', this.scope, 'stream'])) { return; } this.addFollowButtons(); this.listenTo(this.model, 'change:isFollowed', () => { this.controlFollowButtons(); }); } /** @private */ addFollowButtons() { let isFollowed = this.model.get('isFollowed'); this.addMenuItem('buttons', { name: 'unfollow', label: 'Followed', style: 'success', action: 'unfollow', hidden: !isFollowed, }, true); this.addMenuItem('buttons', { name: 'follow', label: 'Follow', style: 'default', iconHtml: '', text: this.translate('Follow'), action: 'follow', hidden: isFollowed || !this.model.has('isFollowed') || !this.getAcl().checkModel(this.model, 'stream'), }, true); } /** @private */ controlFollowButtons() { let isFollowed = this.model.get('isFollowed'); if (isFollowed) { this.hideHeaderActionItem('follow'); this.showHeaderActionItem('unfollow'); return; } this.hideHeaderActionItem('unfollow'); if (this.getAcl().checkModel(this.model, 'stream')) { this.showHeaderActionItem('follow'); } } // noinspection JSUnusedGlobalSymbols /** * Action 'follow'. */ actionFollow() { this.disableMenuItem('follow'); Espo.Ajax .putRequest(this.entityType + '/' + this.model.id + '/subscription') .then(() => { this.hideHeaderActionItem('follow'); this.model.set('isFollowed', true, {sync: true}); this.enableMenuItem('follow'); }) .catch(() => { this.enableMenuItem('follow'); }); } // noinspection JSUnusedGlobalSymbols /** * Action 'unfollow'. */ actionUnfollow() { this.disableMenuItem('unfollow'); Espo.Ajax .deleteRequest(this.entityType + '/' + this.model.id + '/subscription') .then(() => { this.hideHeaderActionItem('unfollow'); this.model.set('isFollowed', false, {sync: true}); this.enableMenuItem('unfollow'); }) .catch(() => { this.enableMenuItem('unfollow'); }); } /** * @inheritDoc */ getHeader() { let name = this.model.get('name') || this.model.id; let $name = $('') .addClass('font-size-flexible title') .text(name); if (this.model.get('deleted')) { $name.css('text-decoration', 'line-through'); } let headerIconHtml = this.getHeaderIconHtml(); let scopeLabel = this.getLanguage().translate(this.scope, 'scopeNamesPlural'); let $root = $('').text(scopeLabel); if (!this.rootLinkDisabled) { $root = $('') .append( $('') .attr('href', this.rootUrl) .addClass('action') .attr('data-action', 'navigateToRoot') .text(scopeLabel) ); } if (headerIconHtml) { $root.prepend(headerIconHtml); } return this.buildHeaderHtml([ $root, $name, ]); } /** * @inheritDoc */ updatePageTitle() { if (this.model.has('name')) { this.setPageTitle(this.model.get('name') || this.model.id); return; } super.updatePageTitle(); } /** * @return {module:views/record/detail} */ getRecordView() { return this.getView('record'); } /** * Update a relationship panel (fetch data). * * @param {string} name A relationship name. */ updateRelationshipPanel(name) { let bottom = this.getView('record').getView('bottom'); if (bottom) { let rel = bottom.getView(name); if (rel) { rel.collection.fetch(); } } } /** * @deprecated Use metadata clientDefs > {EntityType} > relationshipPanels > {link} > createAttributeMap. * @type {Object} */ relatedAttributeMap = {} /** * @deprecated Use clientDefs > {EntityType} > relationshipPanels > {link} > createHandler. * @type {Object} */ relatedAttributeFunctions = {} /** * @deprecated Use clientDefs > {EntityType} > relationshipPanels > {link} > selectHandler. * @type {Object} */ selectRelatedFilters = {} /** * @deprecated Use clientDefs > {EntityType} > relationshipPanels > {link} > selectHandler or * clientDefs > {EntityType} > relationshipPanels > {link} > selectPrimaryFilter. * @type {Object} */ selectPrimaryFilterNames = {} /** * @deprecated Use clientDefs > {EntityType} > relationshipPanels > {link} > selectHandler or * clientDefs > {EntityType} > relationshipPanels > {link} > selectBoolFilterList. * @type {Object} */ selectBoolFilterLists = [] /** * Action 'createRelated'. * * @param {Object} data */ actionCreateRelated(data) { data = data || {}; let link = data.link; let scope = this.model.defs['links'][link].entity; let foreignLink = this.model.defs['links'][link].foreign; let attributes = {}; if ( this.relatedAttributeFunctions[link] && typeof this.relatedAttributeFunctions[link] === 'function' ) { attributes = _.extend(this.relatedAttributeFunctions[link].call(this), attributes); } let attributeMap = this.getMetadata() .get(['clientDefs', this.scope, 'relationshipPanels', link, 'createAttributeMap']) || this.relatedAttributeMap[link] || {}; Object.keys(attributeMap) .forEach(attr => { attributes[attributeMap[attr]] = this.model.get(attr); }); Espo.Ui.notify(' ... '); let handler = this.getMetadata() .get(['clientDefs', this.scope, 'relationshipPanels', link, 'createHandler']); new Promise(resolve => { if (!handler) { resolve({}); return; } Espo.loader.requirePromise(handler) .then(Handler => new Handler(this.getHelper())) .then(handler => { handler.getAttributes(this.model) .then(attributes => resolve(attributes)); }); }).then(additionalAttributes => { attributes = {...attributes, ...additionalAttributes}; let viewName = this.getMetadata() .get(['clientDefs', scope, 'modalViews', 'edit']) || 'views/modals/edit'; this.createView('quickCreate', viewName, { scope: scope, relate: { model: this.model, link: foreignLink, }, attributes: attributes, }, view => { view.render(); view.notify(false); this.listenToOnce(view, 'after:save', () => { if (data.fromSelectRelated) { setTimeout(() => this.clearView('dialogSelectRelated'), 25); } this.updateRelationshipPanel(link); this.model.trigger('after:relate'); this.model.trigger('after:relate:' + link); }); }); }); } // noinspection JSUnusedGlobalSymbols /** * Action 'selectRelated'. * * @param {Object.} data */ actionSelectRelated(data) { let link = data.link; if (!data.foreignEntityType && !this.model.defs['links'][link]) { throw new Error('Link ' + link + ' does not exist.'); } let scope = data.foreignEntityType || this.model.defs['links'][link].entity; let massRelateEnabled = data.massSelect; /** @var {Object.} */ let panelDefs = this.getMetadata().get(['clientDefs', this.scope, 'relationshipPanels', link]) || {}; let advanced = {}; if (link in this.selectRelatedFilters) { advanced = Espo.Utils.cloneDeep(this.selectRelatedFilters[link]) || advanced; for (let filterName in advanced) { if (typeof advanced[filterName] === 'function') { let filtersData = advanced[filterName].call(this); if (filtersData) { advanced[filterName] = filtersData; } else { delete advanced[filterName]; } } } } let foreignLink = this.model.getLinkParam(link, 'foreign'); if (foreignLink && scope) { // Select only records not related with any. let foreignLinkType = this.getMetadata() .get(['entityDefs', scope, 'links', foreignLink, 'type']); let foreignLinkFieldType = this.getMetadata() .get(['entityDefs', scope, 'fields', foreignLink, 'type']); if ( ~['belongsTo', 'belongsToParent'].indexOf(foreignLinkType) && foreignLinkFieldType && !advanced[foreignLink] && ~['link', 'linkParent'].indexOf(foreignLinkFieldType) ) { advanced[foreignLink] = { type: 'isNull', attribute: foreignLink + 'Id', data: { type: 'isEmpty', }, }; } } let primaryFilterName = data.primaryFilterName || this.selectPrimaryFilterNames[link] || null; if (typeof primaryFilterName === 'function') { primaryFilterName = primaryFilterName.call(this); } let dataBoolFilterList = data.boolFilterList; if (typeof data.boolFilterList === 'string') { dataBoolFilterList = data.boolFilterList.split(','); } let boolFilterList = dataBoolFilterList || panelDefs.selectBoolFilterList || this.selectBoolFilterLists[link]; if (typeof boolFilterList === 'function') { boolFilterList = boolFilterList.call(this); } boolFilterList = Espo.Utils.clone(boolFilterList); primaryFilterName = primaryFilterName || panelDefs.selectPrimaryFilterName || null; let viewKey = data.viewKey || 'select'; let viewName = panelDefs.selectModalView || this.getMetadata().get(['clientDefs', scope, 'modalViews', viewKey]) || 'views/modals/select-records'; Espo.Ui.notify(' ... '); let handler = panelDefs.selectHandler || null; new Promise(resolve => { if (!handler) { resolve({}); return; } Espo.loader.requirePromise(handler) .then(Handler => new Handler(this.getHelper())) .then(/** module:handlers/select-related */handler => { handler.getFilters(this.model) .then(filters => resolve(filters)); }); }).then(filters => { advanced = {...advanced, ...(filters.advanced || {})}; if (boolFilterList || filters.bool) { boolFilterList = [ ...(boolFilterList || []), ...(filters.bool || []), ]; } if (filters.primary && !primaryFilterName) { primaryFilterName = filters.primary; } this.createView('dialogSelectRelated', viewName, { scope: scope, multiple: true, createButton: data.createButton || false, triggerCreateEvent: true, filters: advanced, massRelateEnabled: massRelateEnabled, primaryFilterName: primaryFilterName, boolFilterList: boolFilterList, mandatorySelectAttributeList: panelDefs.selectMandatoryAttributeList, layoutName: panelDefs.selectLayout, }, dialog => { dialog.render(); Espo.Ui.notify(false); this.listenTo(dialog, 'create', () => { this.actionCreateRelated({ link: data.link, fromSelectRelated: true, }); }); this.listenToOnce(dialog, 'select', (selectObj) => { let data = {}; if (Object.prototype.toString.call(selectObj) === '[object Array]') { let ids = []; selectObj.forEach(model => ids.push(model.id)); data.ids = ids; } else { if (selectObj.massRelate) { data.massRelate = true; data.where = selectObj.where; data.searchParams = selectObj.searchParams; } else { data.id = selectObj.id; } } let url = this.scope + '/' + this.model.id + '/' + link; Espo.Ajax.postRequest(url, data) .then(() => { Espo.Ui.success(this.translate('Linked')) this.updateRelationshipPanel(link); this.model.trigger('after:relate'); this.model.trigger('after:relate:' + link); }); }); }); }); } // noinspection JSUnusedGlobalSymbols /** * Action 'duplicate'. */ actionDuplicate() { Espo.Ui.notify(' ... '); Espo.Ajax .postRequest(this.scope + '/action/getDuplicateAttributes', {id: this.model.id}) .then(attributes => { Espo.Ui.notify(false); let url = '#' + this.scope + '/create'; this.getRouter().dispatch(this.scope, 'create', { attributes: attributes, returnUrl: this.getRouter().getCurrentUrl(), options: { duplicateSourceId: this.model.id, }, }); this.getRouter().navigate(url, {trigger: false}); }); } } export default DetailView;