const compContainer = $(`#${comp.code}`); const boardContent = compContainer.find('.kanban-board-content'); const getRegistryList = async () => { const {registryList} = Cons.getAppStore(); return new Promise(async resolve => { if(registryList) { resolve(registryList); } else { const list = await appAPI.getRegistryList(); list ? resolve(UTILS.parseRegistryList(list)) : resolve(null); } }); } const KanbanBoard = { registryCode: null, registryID: null, registryName: '', rights: [], filterCode: null, formCode: null, formId: null, formName: '', statusDict: null, fields: null, fieldDict: null, countInPart: 5, columns: [], searchString: null, sum: null, pageCode: null, stringLength: 50, getUrlParam: function(column){ const {key, currentPage, countInPart} = column; let param = `?registryCode=${this.registryCode}`; if(this.filterCode) param += `&filterCode=${this.filterCode}`; if(this.fields && this.fields.length > 0) { this.fields.forEach(x => param += `&fields=${x.code}`); param+=`&fields=${this.fieldDict}`; } if(this.searchString) param += `&searchString=${this.searchString}`; if(key) param += `&field=${this.fieldDict}&condition=TEXT_EQUALS&key=${key}`; param += `&pageNumber=${currentPage}&countInPart=${countInPart}`; return param; }, searchInRegistry: async function(param){ return new Promise(resolve => { rest.synergyGet(`api/registry/data_ext${param}`, resolve, err => { console.log(`KanbanBoard ERROR [ searchInRegistry ]: ${JSON.stringify(err)}`); resolve(null); }); }); }, getSumRowInRegistry: async function(fieldCode, key){ let param = `?registryCode=${this.registryCode}`; if(this.filterCode) param += `&filterCode=${this.filterCode}`; if(this.searchString) param += `&searchString=${this.searchString}`; if(key) param += `&field=${this.fieldDict}&condition=TEXT_EQUALS&key=${key}`; param += `&fieldCode=${fieldCode}&countType=sum`; return new Promise(resolve => { rest.synergyGet(`api/registry/count_data${param}`, resolve, err => { console.log(`KanbanBoard ERROR [ getSumRowInRegistry ]: ${JSON.stringify(err)}`); resolve(null); }); }); }, getDictInfo: async function(){ const {code, title, value, color} = this.statusDict; const {columns, items, name} = await appAPI.getDictionary(code); this.columns = []; for(let key in items) { const item = items[key]; const parseItem = { key: item[value]?.value, countInPart: this.countInPart, currentPage: 0 }; if(item.hasOwnProperty(title)) { if(item[title].hasOwnProperty('translations')) { parseItem.name = item[title]?.translations[AS.OPTIONS.locale]; } else { parseItem.name = item[title]?.value; } } if(color) parseItem.color = item[color]?.value; this.columns.push(parseItem); } this.columns.sort((a,b) => a.key - b.key); }, allowDrop: function(event) { event.preventDefault(); }, drag: function(param) { const {event, dataUUID, documentID} = param; const parentColumnID = event.target.dataset.parentcolumnid; event.originalEvent.dataTransfer.setData("dataUUID", dataUUID); event.originalEvent.dataTransfer.setData("documentID", documentID); event.originalEvent.dataTransfer.setData("parentColumnID", parentColumnID); }, openDocument: function(dataUUID, updateData = false) { if(this.rights.includes('rr_read')) { if(updateData) { openFormPlayer(dataUUID, null, () => { this.reset(); this.render(); }); } else { openFormPlayer(dataUUID); } } else { showMessage('У вас нет прав на просмотр', 'warning'); } }, cutString: function(str) { return str.length > this.stringLength ? `${str.substr(0, this.stringLength)}...` : str; }, getTitleBlock: function(text, dataUUID){ const block = $('<div>', {class: 'column-card-block title'}); const blockValue = $('<div>', {class: 'column-card-block-value'}); block.append(blockValue); blockValue.text(this.cutString(text)); blockValue.attr('uk-tooltip', `title: ${text}; duration: 300;`); block.on('click', e => { this.openDocument(dataUUID, true); }); return block; }, getSumBlock: function(sum, prefix){ const block = $('<div>', {class: 'column-card-block sum'}); const blockValue = $('<div>', {class: 'column-card-block-value'}); block.append(blockValue); blockValue.text(`${sum} ${prefix}`); return block; }, getReglinkBlock: function(title, code, fieldValue, fieldKey){ if(!fieldKey.hasOwnProperty(code)) return; const block = $('<div>', {class: 'column-card-block reglink'}); const blockTitle = $('<div>', {class: 'column-card-block-title'}); const blockValue = $('<div>', {class: 'column-card-block-value'}); block.append(blockTitle, blockValue); blockTitle.text(`${title}:`); blockValue.text(fieldValue[code]); block.on('click', async e => { const dataUUID = await appAPI.getAsfDataUUID(fieldKey[code]); this.openDocument(dataUUID); }); return block; }, getUserBlock: function(title, value){ const block = $('<div>', {class: 'column-card-block user'}); const blockTitle = $('<div>', {class: 'column-card-block-title'}); const blockValue = $('<div>', {class: 'column-card-block-value'}); block.append(blockTitle, blockValue); blockTitle.text(`${title}:`); blockValue.text(value); return block; }, getDefaultBlock: function(title, value){ const block = $('<div>', {class: 'column-card-block'}); const blockTitle = $('<div>', {class: 'column-card-block-title'}); const blockValue = $('<div>', {class: 'column-card-block-value'}); block.append(blockTitle, blockValue); blockTitle.text(`${title}:`); blockValue.text(value); return block; }, removeRegistryRow: function(dataUUID, e) { e.preventDefault(); e.target.blur(); UIkit.modal.confirm('Вы действительно хотите удалить запись?').then(() => { Cons.showLoader(); try { rest.synergyGet(`api/registry/delete_doc?dataUUID=${dataUUID}`, res => { if(res.errorCode != '0') throw new Error(res.errorMessage); showMessage("Запись удалена", "success"); this.render(); Cons.hideLoader(); }); } catch (err) { Cons.hideLoader(); showMessage("Произошла ошибка при удалении записи", "error"); console.log(error); } }, () => null); }, contextMenu: function(el, dataRow){ let isDelete = false; if(this.rights.indexOf('rr_delete') !== -1 && dataRow.status != 'STATE_NOT_FINISHED') isDelete = true; el.on('contextmenu', event => { $('.board-context-menu').remove(); $('<div>', {class: 'board-context-menu'}) .css({ "left": event.pageX + 'px', "top": event.pageY + 'px' }) .appendTo('body') .append( $('<ul class="uk-nav-default uk-nav-parent-icon" uk-nav>') .append($(`<li ><a href="javascript:void(0);"><span class="uk-margin-small-right" uk-icon="icon: push"></span>Открыть</a></li>`) .on('click', e => { this.openDocument(dataRow.dataUUID, true); }) ) .append('<li class="uk-nav-divider"></li>') .append($(`<li ${isDelete ? '' : 'class="uk-disabled"'} ><a href="javascript:void(0);"><span class="uk-margin-small-right" uk-icon="icon: trash"></span>Удалить запись</a></li>`) .on('click', e => { isDelete ? this.removeRegistryRow(dataRow.dataUUID, e) : false; }) ) ) .show('fast'); return false; }); }, getColumnCard: function(item, columnID){ const {dataUUID, documentID, fieldValue, fieldKey} = item; const cardContainer = $('<div>', {class: 'column-card', id: `card-${dataUUID}`}); cardContainer.attr('data-uuid', dataUUID); cardContainer.attr('data-documentid', documentID); cardContainer.attr('data-parentcolumnid', columnID); cardContainer.attr('draggable', true); for(let i = 0; i < this.fields.length; i++) { const {title, code, type, prefix = '₸'} = this.fields[i]; if(!fieldValue.hasOwnProperty(code)) continue; switch (type) { case 'title': cardContainer.append(this.getTitleBlock(fieldValue[code], dataUUID)); break; case 'sum': cardContainer.append(this.getSumBlock(fieldValue[code], prefix)); break; case 'reglink': cardContainer.append(this.getReglinkBlock(title, code, fieldValue, fieldKey)); break; case 'user': cardContainer.append(this.getUserBlock(title, fieldValue[code])); break; default: cardContainer.append(this.getDefaultBlock(title, fieldValue[code])); break; } } cardContainer.on("dragstart", event => this.drag({event, dataUUID, documentID})); this.contextMenu(cardContainer, item); return cardContainer; }, renderColumn: async function(column, data){ const {name, key, color, recordsCount} = column; const columnContainer = $('<div>', {class: 'column-container', id: `column-${key}`}); const columnTitle = $('<span>', {class: 'column-title'}); const columnSum = $('<span>', {class: 'column-sum'}); const columData = $('<div>', {class: 'column-data'}); const getRowsButton = $('<span>', {class: 'column-get-rows-button'}); let rowsButtonIsHidden = true; columnContainer.append(columnTitle, columnSum, columData); boardContent.append(columnContainer); if(this.sum) { const {cmp, prefix = '₸'} = this.sum; const sumResult = await this.getSumRowInRegistry(cmp, key); const sum = new Intl.NumberFormat('ru-RU').format(Number(sumResult[`${cmp}_0`]) || 0) + ` ${prefix}`; columnSum.text(sum); } else { columnSum.hide(); } if(color) columnTitle.css('background', color); columnTitle.text(`${name} (${recordsCount})`); columnTitle.attr('uk-tooltip', `title: ${name} (${recordsCount}); duration: 300;`); getRowsButton.text('ещё...'); data.forEach(item => { const card = this.getColumnCard(item, key); columData.append(card); }); if(column.countInPart < recordsCount) { columData.append(getRowsButton); rowsButtonIsHidden = false; getRowsButton.on('click', async e => { Cons.showLoader(); column.currentPage++; const searchResult = await this.searchInRegistry(this.getUrlParam(column)); if(searchResult && searchResult.recordsCount > 0) { searchResult.result.forEach(item => { const card = this.getColumnCard(item, key); columData.append(card); if((column.countInPart * (column.currentPage + 1)) < recordsCount) { getRowsButton.detach().appendTo(columData); rowsButtonIsHidden = false; } else { getRowsButton.remove(); rowsButtonIsHidden = true; } }); } else { getRowsButton.hide(); rowsButtonIsHidden = true; } Cons.hideLoader(); }); } columData.on('drop', async event => { event.preventDefault(); const dataUUID = event.originalEvent.dataTransfer.getData("dataUUID"); const parentColumnID = event.originalEvent.dataTransfer.getData("parentColumnID"); const dataColumn = event.target.closest('.column-data'); const parentColumn = this.columns.find(x => x.key == parentColumnID); if(parentColumn.key == column.key) return; Cons.showLoader(); const mergeResult = await appAPI.mergeFormData({ uuid: dataUUID, data: [{ id: this.fieldDict, type: "listbox", value: column.name, key: column.key }] }); if(mergeResult) { dataColumn.appendChild(document.getElementById(`card-${dataUUID}`)); parentColumn.recordsCount--; column.recordsCount++; columnTitle.text(`${column.name} (${column.recordsCount})`); $(`#column-${parentColumnID} > .column-title`).text(`${parentColumn.name} (${parentColumn.recordsCount})`); $(`#card-${dataUUID}`).attr('data-parentcolumnid', column.key); if(this.sum) { const {cmp, prefix = '₸'} = this.sum; const parentSumResult = await this.getSumRowInRegistry(cmp, parentColumnID); const currentSumResult = await this.getSumRowInRegistry(cmp, key); const parentSum = new Intl.NumberFormat('ru-RU').format(Number(parentSumResult[`${cmp}_0`]) || 0) + ` ${prefix}`; const currentSum = new Intl.NumberFormat('ru-RU').format(Number(currentSumResult[`${cmp}_0`]) || 0) + ` ${prefix}`; columnSum.text(currentSum); $(`#column-${parentColumnID} > .column-sum`).text(parentSum); } if(!rowsButtonIsHidden) getRowsButton.detach().appendTo(columData); } else { showMessage("Произошла ошибка при смене статуса", 'error'); } Cons.hideLoader(); }).on('dragover', this.allowDrop); }, render: async function(){ boardContent.empty(); for(let i = 0; i < this.columns.length; i++) { const column = this.columns[i]; const url = this.getUrlParam(column); const searchResult = await this.searchInRegistry(url); column.recordsCount = searchResult.recordsCount; this.renderColumn(column, searchResult.result); } }, reset: function(){ this.columns.forEach(column => column.currentPage = 0); }, init: async function(params){ Cons.showLoader(); try { const { registryCode, filterCode = null, statusDict, fields, fieldDict, countInPart = 5, sum, searchString = null } = params; if(!registryCode) throw new Error(`Не передан параметр registryCode`); if(!statusDict) throw new Error(`Не передан параметр statusDict`); if(!fieldDict) throw new Error(`Не передан параметр fieldDict`); const registryList = await getRegistryList(); const registry = registryList.find(x => x.registryCode == registryCode); if(registry) { if(!registry.rights.includes("rr_list")) throw new Error(`Нет прав на просмотр данного реестра`); const info = await appAPI.getRegistryInfo(registryCode); this.registryCode = registryCode; this.filterCode = filterCode; this.registryID = info.registryID; this.registryName = registry.registryName; this.rights = registry.rights; this.formCode = info.formCode; this.formId = info.formId; this.formName = info.name; this.statusDict = statusDict; this.fields = fields; this.fieldDict = fieldDict; this.countInPart = countInPart; this.sum = sum; this.searchString = searchString; await this.getDictInfo(); this.render(); Cons.hideLoader(); } else { throw new Error(`Не найдено реестра с кодом: ${registryCode}`); } } catch (err) { Cons.hideLoader(); console.log('ERROR init kanban board', err); showMessage(err.message, 'error'); } } } compContainer.off() .on('renderNewBoard', e => { if(!e.hasOwnProperty('eventParam')) return; KanbanBoard.init(e.eventParam); }).on('updateBoard', e => { KanbanBoard.reset(); KanbanBoard.render(); }).on('searchInRegistry', e => { if(!e.hasOwnProperty('eventParam')) return; if(e.eventParam.searchString && e.eventParam.searchString !== "") { KanbanBoard.searchString = e.eventParam.searchString; } else { KanbanBoard.searchString = null; } KanbanBoard.reset(); KanbanBoard.render(); }).on('changeFilterCode', e => { if(!e.hasOwnProperty('eventParam')) return; if(e.eventParam.filterCode && e.eventParam.filterCode !== "") { KanbanBoard.filterCode = e.eventParam.filterCode; } else { KanbanBoard.filterCode = null; } KanbanBoard.reset(); KanbanBoard.render(); }); $(document).off() .on('contextmenu', () => $('.board-context-menu').remove()) .on('click', () => $('.board-context-menu').remove());