Commit 56b5606e authored by Samir Sadyhov's avatar Samir Sadyhov 🤔

обновление компонентов

parent d0d3d3cf
.wf-versions-container {
width: 100%;
height: 40px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-end;
padding: 5px 20px 0px 20px;
background: #FAFAFA;
border-top: 1px solid #D9D9D9;
transition: 0.3s;
}
.wf-versions-content {
width: 100%;
height: 0px;
min-height: 0;
opacity: 0;
margin-top: 5px;
background: #fff;
overflow: auto;
transition: 0.3s;
}
.wf-versions-content.open {
height: 155px;
opacity: 1;
}
.wf-versions-table thead th {
background: #FAFAFA;
border: 1px solid #E8E8E8;
font-weight: 600;
font-size: 12px;
line-height: 12px;
color: #262626;
text-transform: none;
}
.wf-versions-table tbody tr {
background: #fff;
cursor: pointer;
border: none;
color: #595959;
user-select: none;
transition: 0.3s;
}
.wf-versions-table tbody tr.select {
background: #1890FF;
color: #fff;
}
.wf-versions-table tbody tr:hover {
background: rgba(24, 144, 255, 0.8);
color: #fff;
}
.wf-versions-table tbody td {
border: 1px solid #E8E8E8;
padding: 4px 12px;
font-size: 12px;
line-height: 12px;
}
.wf-versions-table .uk-button {
line-height: 20px;
font-size: 12px;
margin-right: 10px;
}
.wf-versions-table .uk-button:last-child {
margin-right: 0;
}
.wf-button-version {
border-radius: 3px;
height: 30px;
display: flex;
align-items: center;
}
.wf-form-buttons-panel {
width: 100%;
height: 50px;
overflow: hidden;
background: #FAFAFA;
border-bottom: 1px solid #D9D9D9;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
}
.wf-form-buttons-panel div {
display: flex;
align-items: center;
height: 100%;
gap: 15px;
}
.wf-form-player-container {
height: calc(100% - 90px);
width: 100%;
overflow: auto;
background: #fff;
padding: 30px;
transition: 0.3s;
}
.wf-other-file-container {
width: 100%;
height: calc(100% - 40px);
padding: 30px;
text-align: center;
transition: 0.3s
}
.wf-icons {
color: #999;
cursor: pointer;
transition: 0.3s;
font-size: 1.8em;
}
.wf-icons:hover {
color: #333;
}
/*
const file = new AttachmentFile(name, uuid, container);
file.open();
*/
const getFileModels = playerModel => {
const result = [];
playerModel.models[0].modelBlocks[0].forEach(block => {
if(block.asfProperty.type === "file") result.push(block);
if(block.asfProperty.type === "table") {
if(block.modelBlocks.length > 0) {
block.modelBlocks.forEach(row => {
row.forEach(tableBlock => {
if(tableBlock.asfProperty.type === "file") result.push(tableBlock);
});
});
}
}
});
return result;
}
const initOpenFilesInForm = player => {
const fileModels = getFileModels(player.model);
fileModels.forEach(fileModel => {
const props = fileModel.asfProperty;
const tmpView = player.view.getViewWithId(props.id, props.ownerTableId, props.tableBlockIndex);
//отключаем все стандартные события
setTimeout(() => {
player.view.container.find(`[data-asformid="file.filename.${props.id}"]`).off();
fileModel.on('valueChange', () => {
player.view.container.find(`[data-asformid="file.filename.${props.id}"]`).off();
return null;
});
}, 100);
//вешаем свое событие
if(tmpView) {
tmpView.container.off().on('click', () => {
if(!fileModel.value) return;
if(!fileModel.value.hasOwnProperty('identifier')) return;
const file = new AttachmentFile(fileModel.value.name, fileModel.value.identifier);
file.open();
});
}
});
}
const initDataLoadPlayer = player => {
player.model.on('dataLoad', () => {
initOpenFilesInForm(player);
});
}
this.AttachmentFile = class {
constructor(name, uuid, container) {
this.fullName = name;
this.identifier = uuid;
this.container = container;
this.init();
}
textFiles = ['doc', 'docx', 'odt', 'xls', 'xlsx', 'ods', 'rtf', 'pdf', 'txt', 'xml', 'js', 'css'];
imgFiles = ['jpg', 'jpeg', 'png', 'svg', 'bmp', 'gif'];
audioFiles = ['wav', 'mp3', 'ogg'];
videoFiles = ['mp4', 'mpeg', 'avi'];
mediaFiles = [...this.imgFiles, ...this.audioFiles, ...this.videoFiles];
async getFileVersions(){
const headers = [i18n.tr('Дата'), i18n.tr('Автор'), i18n.tr('Длина'), i18n.tr('Комментарий'), i18n.tr('Действие')];
const table = $('<table>', {class: "uk-table uk-table-small wf-versions-table uk-table-responsive"});
const thead = $('<thead>');
const tbody = $('<tbody>');
const theadTr = $('<tr>');
headers.forEach(header => theadTr.append(`<th>${header}</th>`));
thead.append(theadTr);
table.append(thead).append(tbody);
const description = await appAPI.getFileDescription(this.identifier);
if(description && description.hasOwnProperty('versions')) {
description.versions.forEach(item => {
const {modified, author, length, comment} = item;
const tr = $('<tr>');
tbody.append(tr);
tr.on('click', e => {
tbody.find('tr').removeClass('select');
tr.addClass('select');
});
tr.append(
`<td>${modified}</td>`,
`<td>${author}</td>`,
`<td>${i18n.tr('{0} Байт').replace('{0}', length)}</td>`,
`<td>${comment}</td>`
);
const lastTd = $('<td>');
const buttonDownloadVersion = $('<button>', {class: 'uk-button uk-button-default uk-button-small button-switch pressed'});
buttonDownloadVersion.text(i18n.tr('Скачать'));
buttonDownloadVersion.on('click', e => {
e.preventDefault();
e.target.blur();
UTILS.fileDownload(this.fullName, item.identifier);
});
lastTd.append(buttonDownloadVersion);
tr.append(lastTd);
});
}
return table;
}
async getContentVersionsFile() {
const container = $('<div>', {class: 'wf-versions-container'});
const content = $('<div>', {class: 'wf-versions-content'});
const button = $('<button>', {class: 'uk-button uk-button-default button-switch wf-button-version'});
button.text(i18n.tr('Версии'));
container.append(button, content);
const table = await this.getFileVersions();
content.append(table);
return {container, button, content};
}
async canEdit(workInfo) {
const {docInfo, actionID, has_subprocesses, parent_process} = workInfo;
const procCode = ['approval-single', 'agreement-single', 'acquaintance-single'];
if(!docInfo) return false;
if(docInfo.registered == "true") return false;
if(procCode.includes(parent_process)) return false;
if(has_subprocesses == "true") {
const subworks = await appAPI.getSubworks(actionID);
const filtered = subworks.filter(x => procCode.includes(x.parent_process));
if(filtered.length) return false;
}
return true;
}
async getContentFromForm(asfData) {
Cons.showLoader();
const {uuid, version} = asfData;
const documentID = await AS.FORMS.ApiUtils.getDocumentIdentifier(uuid);
const docInfo = await appAPI.getDocumentInfo(documentID);
let workInfo = {};
if(docInfo.actions.length) workInfo = await appAPI.getWorkInfo(docInfo.actions[0]);
workInfo.docInfo = docInfo;
const can_edit = await this.canEdit(workInfo);
Cons.hideLoader();
const container = $('<div>', {style: 'width: 100%; overflow: hidden;'});
if(this.container) {
container.css('height', '100%');
} else {
container.css('height', 'calc(100% - 40px)');
}
const buttonsPanel = $('<div>', {class: 'wf-form-buttons-panel'});
const playerContainer = $('<div>', {class: 'wf-form-player-container'});
const buttonSave = $('<span class="material-icons wf-icons" style="display: none;">save</span>');
const buttonPrint = $('<span class="material-icons wf-icons">print</span>');
const buttonEditable = $('<span class="material-icons wf-icons">edit</span>');
if(!can_edit) {
buttonSave.hide();
buttonEditable.hide();
this.editable = false;
} else if (this.editable) {
buttonEditable.text('description');
buttonPrint.hide();
buttonSave.show();
}
const player = UTILS.getSynergyPlayer(uuid, this.editable, version);
player.view.container.css({'background': '#fff'});
playerContainer.append(player.view.container);
this.formPlayer = player;
initDataLoadPlayer(player);
buttonSave.on('click', e => {
if(!player.model.hasChanges) return;
Cons.showLoader();
if(!player.model.isValid()) {
showMessage(i18n.tr('Заполните обязательные поля'), 'error');
Cons.hideLoader();
} else {
player.saveFormData(result => {
showMessage(i18n.tr('Данные сохранены'), 'success');
Cons.hideLoader();
});
}
});
buttonPrint.on('click', e => {
if(player.model.hasPrintable) {
window.open(`../Synergy/rest/asforms/template/print/form?format=pdf&dataUUID=${uuid}`);
} else {
UTILS.printForm(player.view.container[0]);
}
});
buttonEditable.on('click', e => {
this.editable = !this.editable;
player.view.setEditable(this.editable);
if(this.editable) {
buttonEditable.text('description');
buttonPrint.hide();
buttonSave.show();
} else {
buttonEditable.text('edit');
buttonPrint.show();
if(player.model.hasChanges) {
buttonSave.show();
} else {
buttonSave.hide();
}
}
initDataLoadPlayer(player);
});
buttonsPanel.append(
$('<div>').append(buttonSave, buttonPrint),
$('<div>').append(buttonEditable)
);
const {container: footer, button: buttonVersion, content: contentVersion} = await this.getContentVersionsFile();
let panelVersion = false;
buttonVersion.off().on('click', e => {
e.preventDefault();
e.target.blur();
if(panelVersion) {
playerContainer.css({"height": "calc(100% - 90px)"});
footer.css({"height": "40px"});
buttonVersion.removeClass('pressed');
contentVersion.removeClass('open');
} else {
playerContainer.css({"height": "calc(100% - 250px)"});
footer.css({"height": "250px"});
buttonVersion.addClass('pressed');
contentVersion.addClass('open');
}
panelVersion = !panelVersion;
});
container.append(buttonsPanel, playerContainer, footer);
return container;
}
async getContentFromFile(src) {
const container = $('<div>', {style: 'width: 100%; overflow: hidden;'});
if(this.container) {
container.css('height', '100%');
} else {
container.css('height', 'calc(100% - 40px)');
}
const iFrame = $(`<iframe id="file-view" src="${src}">`);
iFrame.css({"width": "100%", "height": "calc(100% - 40px)", "transition": "0.3s"});
const {container: footer, button: buttonVersion, content: contentVersion} = await this.getContentVersionsFile();
let panelVersion = false;
buttonVersion.off().on('click', e => {
e.preventDefault();
e.target.blur();
if(panelVersion) {
iFrame.css({"height": "calc(100% - 40px)"});
footer.css({"height": "40px"});
buttonVersion.removeClass('pressed');
contentVersion.removeClass('open');
} else {
iFrame.css({"height": "calc(100% - 250px)"});
footer.css({"height": "250px"});
buttonVersion.addClass('pressed');
contentVersion.addClass('open');
}
panelVersion = !panelVersion;
});
container.append(iFrame, footer);
return container;
}
async getContentFromMedia(src) {
const container = $('<div>', {style: 'width: 100%; overflow: hidden;'});
if(this.container) {
container.css('height', '100%');
} else {
container.css('height', 'calc(100% - 40px)');
}
const body = $(`<div>`);
body.css({
"width": "100%",
"height": "calc(100% - 40px)",
"overflow": "hidden",
"display": "flex",
"justify-content": "center",
"transition": "0.3s"
});
if(this.imgFiles.includes(this.ext)) {
body.append(`<img src="${src}" alt="" style="object-fit: scale-down; width: 100%; height: 100%;">`);
} else if (this.audioFiles.includes(this.ext)) {
body
.css({"align-items": "center"})
.append(`
<audio controls>
<source src="${src}">
<a href="${src}">Скачать</a>
</audio>`);
} else if (this.videoFiles.includes(this.ext)) {
body
.css({"align-items": "center"})
.append(`
<video controls="controls" style="width: auto; height: 100%;">
<source src="${src}">
<a href="${src}">Скачать</a>
</video>`);
}
const {container: footer, button: buttonVersion, content: contentVersion} = await this.getContentVersionsFile();
let panelVersion = false;
buttonVersion.off().on('click', e => {
e.preventDefault();
e.target.blur();
if(panelVersion) {
body.css({"height": "calc(100% - 40px)"});
footer.css({"height": "40px"});
buttonVersion.removeClass('pressed');
contentVersion.removeClass('open');
} else {
body.css({"height": "calc(100% - 250px)"});
footer.css({"height": "250px"});
buttonVersion.addClass('pressed');
contentVersion.addClass('open');
}
panelVersion = !panelVersion;
});
container.append(body, footer);
return container;
}
async openASForm() {
Cons.showLoader();
const asfData = await appAPI.getFile(this.identifier, 'json');
const body = await this.getContentFromForm(asfData);
if(this.container) {
this.container.empty();
this.container.append(body);
} else {
const dialog = UTILS.getFullModalDialog(this.nameWithExt, body);
UIkit.modal(dialog).show();
dialog.on('hidden', () => dialog.remove());
}
Cons.hideLoader();
}
async openPDF() {
Cons.showLoader();
const file = await appAPI.getPdfFile(this.identifier);
if(!file) {
Cons.hideLoader();
return;
}
const fileUrl = window.URL.createObjectURL(file);
const body = await this.getContentFromFile(fileUrl, this.identifier);
if(this.container) {
this.container.empty();
this.container.append(body);
} else {
const dialog = UTILS.getFullModalDialog(this.fullName, body);
UIkit.modal(dialog).show();
dialog.on('hidden', () => dialog.remove());
}
Cons.hideLoader();
}
async openMedia() {
Cons.showLoader();
const file = await appAPI.getFile(this.identifier);
if(!file) {
Cons.hideLoader();
return;
}
const fileUrl = window.URL.createObjectURL(file);
const body = await this.getContentFromMedia(fileUrl);;
if(this.container) {
this.container.empty();
this.container.append(body);
} else {
const dialog = UTILS.getFullModalDialog(this.fullName, body);
UIkit.modal(dialog).show();
dialog.on('hidden', () => dialog.remove());
}
Cons.hideLoader();
}
async openDownload() {
const container = $('<div>', {style: 'width: 100%; overflow: hidden;'});
if(this.container) {
container.css('height', '100%');
} else {
container.css('height', 'calc(100% - 40px)');
}
const body = $('<div>', {class: 'wf-other-file-container'});
const button = $('<button>', {class: 'uk-button uk-button-primary', style: 'border-radius: 3px;'});
button.text(i18n.tr('Скачать'));
body.append(
`<p>${i18n.tr('Уважаемый {0}!').replace('{0}', UTILS.getCurrentUserFullName())}</p>`,
`<p>${i18n.tr('Данный файл имеет неизвестный для системы формат, и просмотреть его в браузере не представляется возможным.')}</p>`,
`<p>${i18n.tr('Вы можете скачать этот файл себе на компьютер и попробовать открыть его.')}</p>`,
button
);
button.on('click', e => {
e.preventDefault();
e.target.blur();
Cons.showLoader();
UTILS.fileDownload(this.fullName, this.identifier);
Cons.hideLoader();
});
const {container: footer, button: buttonVersion, content: contentVersion} = await this.getContentVersionsFile();
let panelVersion = false;
buttonVersion.off().on('click', e => {
e.preventDefault();
e.target.blur();
if(panelVersion) {
body.css({"height": "calc(100% - 40px)"});
footer.css({"height": "40px"});
buttonVersion.removeClass('pressed');
contentVersion.removeClass('open');
} else {
body.css({"height": "calc(100% - 250px)"});
footer.css({"height": "250px"});
buttonVersion.addClass('pressed');
contentVersion.addClass('open');
}
panelVersion = !panelVersion;
});
container.append(body, footer);
if(this.container) {
this.container.empty();
this.container.append(container);
} else {
const dialog = UTILS.getFullModalDialog(this.fullName, container);
UIkit.modal(dialog).show();
dialog.on('hidden', () => dialog.remove());
}
}
async open(){
if(this.ext == 'asfdocx') {
await this.openASForm();
} else if (this.textFiles.includes(this.ext)) {
await this.openPDF();
} else if (this.mediaFiles.includes(this.ext)) {
await this.openMedia();
} else {
await this.openDownload();
}
}
init(){
this.ext = this.fullName.substring(this.fullName.lastIndexOf('.') + 1);
this.nameWithExt = this.fullName.substr(0, this.fullName.lastIndexOf('.'));
}
}
......@@ -182,11 +182,9 @@ const favoritesInit = (items, successHandler) => {
}
this.QuickAccessMenu = class {
constructor(buttonContainerSelector, defaultHandler, defaultHandlerTitle, addMenuItems) {
constructor(buttonContainerSelector, defaultHandler) {
this.buttonContainerSelector = buttonContainerSelector;
this.defaultHandler = defaultHandler;
this.defaultHandlerTitle = defaultHandlerTitle;
this.addMenuItems = addMenuItems;
this.init();
}
......@@ -196,6 +194,8 @@ this.QuickAccessMenu = class {
e.preventDefault();
if(this.defaultHandler && typeof this.defaultHandler === 'function') {
this.defaultHandler();
} else {
this.openMenuWindow();
}
});
......@@ -366,7 +366,7 @@ this.QuickAccessMenu = class {
}
});
const searchFilterItems = [{title: 'Любое совпадение слов', value: 'OR'}, {title: 'Содержатся все слова', value: 'AND'}];
const searchFilterItems = [{title: i18n.tr('Любое совпадение слов'), value: 'OR'}, {title: i18n.tr('Содержатся все слова'), value: 'AND'}];
const selectFilterSearch = new CustomSelect({items: searchFilterItems, defaultValue: this._search.filterCondition, classList: 'qam_filter_select_search'});
const selectFilterLabel = $('<span>', {class: 'uk-text-bolder'});
selectFilterLabel.text(i18n.tr('Условие поиска:'));
......
.resolution_body {
height: calc(100% - 90px);
padding: 0px;
}
.resolution_footer {
display: flex;
justify-content: end;
gap: 10px;
padding: 10px;
}
.resolution_panel_actions {
display: flex;
justify-content: space-between;
width: 100%;
padding: 10px;
}
.resolution_panel_items {
width: calc(100% - 10px);
height: calc(100% - 50px);
border: 1px solid #c4c4c4;
margin: 0 5px;
padding: 5px;
overflow-x: hidden;
overflow-y: auto;
}
.resolution_item {
display: flex;
width: 100%;
min-height: 50px;
border: 1px solid #e5e5e5;
background: #fff;
margin-bottom: 5px;
}
.resolution_item.active {
border: 1px solid #999;
background: #f9f9f9;
}
.resolution_item>div {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
gap: 5px;
}
.resolution_item>div:first-child {
width: 100%;
padding: 5px;
}
.resolution_item>div:last-child {
display: none;
}
.resolution_item.active>div:first-child {
width: calc(100% - 30px);
}
.resolution_item.active>div:last-child {
display: block;
width: 30px;
}
.resolution_item_name {
width: 100%;
}
.resolution_item_name>input,
.resolution_item_param>input,
.resolution_item_param>div,
.resolution_item_users>div {
display: none;
}
.resolution_item.active .resolution_item_name>input,
.resolution_item.active .resolution_item_param>input,
.resolution_item.active .resolution_item_param>div,
.resolution_item.active .resolution_item_users>div {
display: block;
}
.resolution_item.active .resolution_item_name>span,
.resolution_item.active .resolution_item_param>span,
.resolution_item.active .resolution_item_users>span {
display: none;
}
.resolution_item_param {
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.resolution_item.active .resolution_item_param {
flex-direction: row;
align-items: center;
}
.resolution_datetime_input_container {
display: none;
flex-direction: row;
}
.resolution_item.active .resolution_item_param .resolution_datetime_input_container {
display: flex;
}
.resolution_panel_items a.uk-icon[disabled] {
pointer-events: none;
}
.resolution_item .uk-select[disabled] {
background-color: #fff;
opacity: 1;
}
.resolution_item_param .uk-checkbox[disabled],
.resolution_item .resolution_datetime_input_container input[disabled]{
background-color: #fff;
}
.resolution_item *,
.resolution_item ::placeholder {
font-size: 14px
}
const getInputDateTime = (d, t) => $(`<input type="date" class="uk-input" style="max-width: 140px;" value="${d || ''}"><input type="time" class="uk-input" style="max-width: 100px;" value="${t || ''}">`);
const createDateTimeInput = date => {
if(date) date = date.split(' ');
const container = $('<div>', {class: 'resolution_datetime_input_container'});
const dateTimeInput = date ? getInputDateTime(date[0], date[1]) : getInputDateTime();
container.append(dateTimeInput);
return {container, dateTimeInput};
}
const createSelectComponent = (items, multiple = false) => {
const container = $('<div>', {class: 'uk-form-controls'});
const select = $('<select class="uk-select" style="min-width: 100px;">');
if(multiple) select.attr('multiple', 'multiple');
if(items) items.sort((a,b) => a.value - b.value)
.forEach(item => select.append(`<option value="${item.value}" title="${item.label}">${item.label}</option>`));
container.append(select);
return {container, select};
}
const createUserParamBlock = (labelUsers, placeholder, type, item, multiSelectable = false, filterDepartmentID = null) => {
const container = $('<div>', {class: 'uk-inline uk-width-expand', style: "min-width: 170px;"});
const button = $('<a class="uk-form-icon uk-form-icon-flip" href="javascript:void(0);" uk-icon="icon: users"></a>');
const input = $(`<input class="uk-input" type="text" style="background: #fff;" placeholder="${placeholder}" disabled>`);
let values = null;
if(type == 'responsible') {
if(item.userID) values = [{personName: item.user, personID: item.userID}];
} else if (type == 'users') {
values = item.users.map(x => ({personID: x.userID, personName: x.user}));
}
container.append(button, input);
if(values) {
let userNames = values.map(x => x.personName).join('; ');
input.val(userNames).attr('title', userNames);
labelUsers.text(`${placeholder}: ${userNames}`);
} else {
labelUsers.text(`${placeholder}:`);
}
button.on('click', e => {
//(values, multiSelectable, isGroupSelectable, showWithoutPosition, filterPositionID, filterDepartmentID, locale, handler)
AS.SERVICES.showUserChooserDialog(values, multiSelectable, false, false, null, filterDepartmentID, AS.OPTIONS.locale, users => {
let userNames = users.map(x => x.personName).join('; ');
input.val(userNames).attr('title', userNames);
labelUsers.text(`${placeholder}: ${userNames}`);
if(type == 'responsible') {
item.user = users[0].personName;
item.userID = users[0].personID;
} else if (type == 'users') {
item.users = users.map(x => ({userID: x.personID, user: x.personName}));
}
values = users;
});
});
return {container, button, input};
}
this.Resolution = class {
constructor(_doc) {
this._doc = _doc;
this.itemID = 0;
this.items = {};
this.actionID = null;
this.documentID = null;
this.init();
}
getModal() {
const dialog = $('<div class="uk-flex-top" uk-modal="bg-close: false; esc-close: false;">');
const md = $('<div>', {class: 'uk-modal-dialog uk-margin-auto-vertical', style: 'width: 900px; height: 500px;'});
const modalBody = $('<div>', {class: 'uk-modal-body resolution_body'});
const footer = $('<div>', {class: 'uk-modal-footer resolution_footer'});
const panelActions = $('<div>', {class: 'resolution_panel_actions'});
this.panelItems = $('<div>', {class: 'resolution_panel_items'});
this.buttonAddResolutionItem = $(`<button class="uk-button uk-button-default uk-button-small" type="button">${i18n.tr("Добавить пункт")}</button>`);
this.buttonPassResolution = $(`<button class="uk-button uk-button-primary uk-button-small" type="button">${i18n.tr("Принять")}</button>`);
this.buttonSaveResolution = $(`<button class="uk-button uk-button-default uk-button-small" type="button">${i18n.tr("Сохранить")}</button>`);
this.isControl = $(`<input class="uk-checkbox" type="checkbox" id="resolution_is_control">`);
if(this.resolution.controlled) this.isControl.prop('checked', true);
modalBody.append(panelActions, this.panelItems);
panelActions.append(
this.buttonAddResolutionItem,
$('<label>', {class: "uk-text-small"}).append(this.isControl, ` ${i18n.tr('Является контрольным')}`)
);
footer.append(this.buttonPassResolution);
if(this.resolution.statusID == 0) footer.append(this.buttonSaveResolution);
md.append(`<button class="uk-modal-close-default modal-close-custom" type="button" uk-close></button>`,
`<div class="uk-modal-header"><h3>${i18n.tr("Резолюция")}</h3></div>`, modalBody, footer);
dialog.append(md);
return dialog;
}
getTmpItem(){
return {
name: null,
userID: null,
user: null,
users: [],
finishdate: {
date: null
},
typeID: null,
completionFormID: null,
completionFormCode: null,
itemID: null,
dict_id: null,
item_name: null
}
}
addResolutionItem(item){
const itemBlock = $('<div>', {class: 'resolution_item active'});
const panel1 = $('<div>');
const panel2 = $('<div>');
const deleteButton = $(`<a href="javascript:void(0)" uk-icon="trash"></a>`);
const itemNameBlock = $('<div>', {class: 'resolution_item_name'});
const labelName = $('<span>', {class: 'resolution_item_name_label uk-text-normal'});
const inputName = $('<input>', {class: "uk-input", type: "text", placeholder: i18n.tr("Введите формулировку пункта резолюции")});
const datalist = $('<datalist>');
let tmpItemID = `item-${this.itemID}`;
if(item) {
tmpItemID = item.itemID;
inputName.val(item.name);
labelName.text(item.name);
} else {
const tmpItem = this.getTmpItem();
tmpItem.dict_id = this.workTypes[0].dict_id;
tmpItem.item_name = this.workTypes[0].item_name;
tmpItem.typeID = 3;
this.items[tmpItemID] = tmpItem;
this.itemID++;
}
inputName.attr('list', tmpItemID);
datalist.attr('id', tmpItemID);
const itemParamBlock = $('<div>', {class: 'resolution_item_param'});
const responsibleCheck = $(`<input class="uk-checkbox" type="checkbox">`);
const labelUser = $('<span>', {class: 'resolution_item_users_label uk-text-small uk-text-bold'});
const {container: userContainer, button: userButton, input: userInput} = createUserParamBlock(labelUser, i18n.tr("Ответственный"), 'responsible', this.items[tmpItemID]);
const {container: workFinishContainer, dateTimeInput} = createDateTimeInput();
const labelFinishDate = $('<span>', {class: 'resolution_item_finishdate_label uk-text-small uk-text-bold'});
const wtList = this.workTypes.map(x => ({label: x.item_name, value: x.dict_id}));
const {container: workTypeContainer, select: workTypeSelect} = createSelectComponent(wtList);
const labelWT = $('<span>', {class: 'resolution_item_work_type_label uk-text-small uk-text-bold'});
const cfList = this.completionForms.map(x => ({label: x.name, value: x.id}));
cfList.unshift({label: i18n.tr('Результат работы'), value: 'empty'});
const {container: cfContainer, select: cfSelect} = createSelectComponent(cfList);
const labelCF= $('<span>', {class: 'resolution_item_cf_label uk-text-small uk-text-bold'});
const itemUsersBlock = $('<div>', {class: 'resolution_item_users'});
const labelUsers = $('<span>', {class: 'resolution_item_users_label uk-text-small uk-text-bold'});
const {container: usersContainer} = createUserParamBlock(labelUsers, i18n.tr("Исполнители"), 'users', this.items[tmpItemID], true);
if(this.items[tmpItemID].finishdate.date) {
let finishDate = new Date(Number(this.items[tmpItemID].finishdate.date));
finishDate = AS.FORMS.DateUtils.formatDate(finishDate, AS.FORMS.DateUtils.DATE_FORMAT_FULL);
finishDate = finishDate.split(' ');
$(dateTimeInput[0]).val(finishDate[0]);
$(dateTimeInput[1]).val(finishDate[1].slice(0,5));
} else if(this.completion_date_for_resolution) {
//Подставлять дату завершения документа в пункты резолюции
let finishDate = this.resolution.maxFinishDate.split(' ');
$(dateTimeInput[0]).val(finishDate[0]);
$(dateTimeInput[1]).val(finishDate[1].slice(0,5));
this.items[tmpItemID].finishdate.date = String(new Date(this.resolution.maxFinishDate).getTime());
}
const wtItem = this.workTypes.find(x => x.dict_id == this.items[tmpItemID].dict_id);
const cfItem = this.completionForms.find(x => x.id == this.items[tmpItemID].completionFormID);
if(this.items[tmpItemID].dict_id) {
workTypeSelect.val(this.items[tmpItemID].dict_id);
} else {
workTypeSelect.val(wtList[0].value);
}
if(this.items[tmpItemID].completionFormID) cfSelect.val(this.items[tmpItemID].completionFormID);
labelFinishDate.text(`${i18n.tr("Завершение")}: ${$(dateTimeInput[0]).val()} ${$(dateTimeInput[1]).val()}`);
if(wtItem) {
labelWT.text(`${i18n.tr("Тип")}: ${wtList.find(x => x.value == this.items[tmpItemID].dict_id).label}`);
} else {
labelWT.text(`${i18n.tr("Тип")}:`);
}
if(cfItem) {
labelCF.text(`${i18n.tr("Форма завершения")}: ${cfList.find(x => x.value == this.items[tmpItemID].completionFormID).label}`);
} else {
labelCF.text(`${i18n.tr("Форма завершения")}:`);
}
this.resolutionText.forEach(x => datalist.append(`<option value="${x.text}"></option>`));
this.panelItems.find('.resolution_item').removeClass('active');
itemNameBlock.append(inputName, datalist, labelName);
itemParamBlock.append(responsibleCheck, userContainer, labelUser, workFinishContainer, labelFinishDate, workTypeContainer, labelWT, cfContainer, labelCF);
itemUsersBlock.append(usersContainer, labelUsers);
panel1.append(itemNameBlock, itemParamBlock, itemUsersBlock);
panel2.append(deleteButton);
itemBlock.append(panel1, panel2);
this.panelItems.append(itemBlock);
if(item) {
responsibleCheck.prop('disabled', true);
workTypeSelect.prop('disabled', true);
userButton.attr('disabled', true);
if(item.userID) responsibleCheck.prop('checked', true);
} else {
responsibleCheck.prop('checked', true);
}
responsibleCheck.on('change', e => {
if(e.target.checked) {
userButton.attr('disabled', false);
} else {
userButton.attr('disabled', true);
labelUser.text(`${i18n.tr("Ответственный")}:`);
this.items[tmpItemID].userID = null;
this.items[tmpItemID].user = '';
userInput.val(null);
}
});
$(dateTimeInput[0]).on('change', e => {
let datetime = '';
if($(dateTimeInput[0]).val() != '') {
datetime = $(dateTimeInput[0]).val();
if($(dateTimeInput[1]).val() != '') {
datetime += ` ${$(dateTimeInput[1]).val()}`;
} else {
$(dateTimeInput[1]).val(this.day_finish_time);
datetime += ` ${this.day_finish_time}`;
}
}
labelFinishDate.text(`${i18n.tr("Завершение")}: ${datetime}`);
this.items[tmpItemID].finishdate.date = String(new Date(datetime).getTime());
});
$(dateTimeInput[1]).on('change', e => {
let datetime = '';
if($(dateTimeInput[0]).val() != '') {
datetime = $(dateTimeInput[0]).val();
if($(dateTimeInput[1]).val() != '') {
datetime += ` ${$(dateTimeInput[1]).val()}`;
} else {
datetime += ` ${this.day_finish_time}`;
}
}
labelFinishDate.text(`${i18n.tr("Завершение")}: ${datetime}`);
this.items[tmpItemID].finishdate.date = String(new Date(datetime).getTime());
});
workTypeSelect.on('change', e => {
const changeFields = () => {
// блокируем фз и юзера
cfSelect.prop('disabled', true).val('empty');
this.items[tmpItemID].completionFormCode = null;
this.items[tmpItemID].completionFormID = null;
labelCF.text(`${i18n.tr("Форма завершения")}:`);
userButton.attr('disabled', true);
labelUser.text(`${i18n.tr("Ответственный")}:`);
this.items[tmpItemID].userID = null;
this.items[tmpItemID].user = '';
userInput.val(null);
responsibleCheck.prop('disabled', true).prop('checked', false);
$(dateTimeInput[0]).attr('disabled', false);
$(dateTimeInput[1]).attr('disabled', false);
}
const wtItem = this.workTypes.find(x => x.dict_id == workTypeSelect.val());
this.items[tmpItemID].dict_id = wtItem.dict_id;
this.items[tmpItemID].item_name = wtItem.item_name;
labelWT.text(`${i18n.tr("Тип")}: ${wtItem.item_name}`);
switch (wtItem.item_number) {
case 1: { //работа
// все открываем
responsibleCheck.prop('disabled', false);
cfSelect.prop('disabled', false);
$(dateTimeInput[0]).attr('disabled', false);
$(dateTimeInput[1]).attr('disabled', false);
this.items[tmpItemID].typeID = 3;
break;
}
case 2: {
changeFields();
this.items[tmpItemID].typeID = 0;
break;
}
case 3: {
changeFields();
this.items[tmpItemID].typeID = 1;
break;
}
case 4: {
changeFields();
this.items[tmpItemID].typeID = 2;
break;
}
case 5: { // Резолюция
// все открываем
userButton.attr('disabled', false);
responsibleCheck.prop('disabled', false).prop('checked', true);
responsibleCheck.prop('disabled', false);
cfSelect.prop('disabled', false);
$(dateTimeInput[0]).attr('disabled', false);
$(dateTimeInput[1]).attr('disabled', false);
this.items[tmpItemID].typeID = 33;
break;
}
case 6: { // Отправка документа
// блокируем юзер, дата, фз
cfSelect.prop('disabled', true).val('empty');
this.items[tmpItemID].completionFormCode = null;
this.items[tmpItemID].completionFormID = null;
labelCF.text(`${i18n.tr("Форма завершения")}:`);
userButton.attr('disabled', true);
labelUser.text(`${i18n.tr("Ответственный")}:`);
this.items[tmpItemID].userID = null;
this.items[tmpItemID].user = '';
userInput.val(null);
responsibleCheck.prop('disabled', true).prop('checked', false);
$(dateTimeInput[0]).attr('disabled', true).val(null);
$(dateTimeInput[1]).attr('disabled', true).val(null);
labelFinishDate.text(`${i18n.tr("Завершение")}:`);
this.items[tmpItemID].finishdate.date = null;
this.items[tmpItemID].typeID = 17;
break;
}
}
});
cfSelect.on('change', e => {
const cfItem = this.completionForms.find(x => x.id == cfSelect.val());
if(cfItem) {
this.items[tmpItemID].completionFormCode = cfItem.code;
this.items[tmpItemID].completionFormID = cfItem.id;
labelCF.text(`${i18n.tr("Форма завершения")}: ${cfItem.name}`);
} else {
this.items[tmpItemID].completionFormCode = null;
this.items[tmpItemID].completionFormID = null;
labelCF.text(`${i18n.tr("Форма завершения")}:`);
}
});
inputName.on('change', e => {
this.items[tmpItemID].name = inputName.val();
labelName.text(this.items[tmpItemID].name);
});
itemBlock.on('click', e => {
this.panelItems.find('.resolution_item').removeClass('active');
itemBlock.addClass('active');
});
deleteButton.on('click', e => {
e.preventDefault();
itemBlock.remove();
this.panelItems.find('.resolution_item').removeClass('active');
this.panelItems.find('.resolution_item:first').addClass('active');
delete this.items[tmpItemID];
this.resolution.items = Object.values(this.items);
});
this.panelItems.scrollTop(this.panelItems.offset().top);
}
getParamURL(){
this.resolution.items = Object.values(this.items);
const params = new URLSearchParams();
const data = this.resolution.items.map(x => ({
name: x.name,
userID: x.userID,
usersID: x.users.map(u => u.userID),
finishDate: x.finishdate.date,
typeID: x.typeID,
completionFormID: x.completionFormID,
completionFormCode: x.completionFormCode,
itemID: x.itemID,
dict_id: x.dict_id
}));
if(this.resolution.projectID) params.append("projectID", this.resolution.projectID);
if(this.actionID) params.append("workID", this.actionID);
if(this.documentID) params.append("documentID", this.documentID);
params.append("controlled", this.resolution.controlled);
params.append("data", JSON.stringify(data));
return params;
}
checkItemFinishDate(item){
const { maxFinishDate } = this.resolution;
const currDate = AS.FORMS.DateUtils.formatDate(new Date(), AS.FORMS.DateUtils.DATE_FORMAT_FULL);
let itemFinishDate = item.finishdate.date;
if(!itemFinishDate) return i18n.tr('Дата завершения некоторых элементов меньше текущей даты');
itemFinishDate = new Date(Number(itemFinishDate));
itemFinishDate = AS.FORMS.DateUtils.formatDate(itemFinishDate, AS.FORMS.DateUtils.DATE_FORMAT_FULL);
if(itemFinishDate < currDate) return i18n.tr('Дата завершения некоторых элементов меньше текущей даты');
if(!this.date_resolution_after_finish_document && itemFinishDate > maxFinishDate) return i18n.tr('Дата завершения каждого из пунктов резолюции не должна превышать срок документа');
return null;
}
checkResolutionItems(){
const result = {errorCode: 0, errorMessage: ''};
const errors = new Set();
for(const itemID in this.items) {
const item = this.items[itemID];
const wtItem = this.workTypes.find(x => x.dict_id == item.dict_id);
const chDate = this.checkItemFinishDate(item);
// проверить формулировку
if(!item.name) {
errors.add(i18n.tr('Не для всех элементов резолюции введены формулировки'));
result.errorCode++;
}
switch (wtItem.item_number) {
// проверить дату, ответсвтвенного (если стоит галочка) и исполнителей (если нет галочки ответственного)
case 1: // работа
case 5: { // Резолюция
if(!item.userID && !item.users.length) {
errors.add(i18n.tr('Не для всех элементов резолюции выбраны ответственные пользователи'));
result.errorCode++;
}
if(chDate) {
errors.add(chDate);
result.errorCode++;
}
break;
}
// проверить исполнителей и дату
case 2: // согласование
case 3: // утверждение
case 4: { // ознакомление
if(!item.users.length) {
errors.add(i18n.tr('Не для всех элементов резолюции выбраны ответственные пользователи'));
result.errorCode++;
}
if(chDate) {
errors.add(chDate);
result.errorCode++;
}
break;
}
// проверить исполнителей
case 6: { // Отправка документа
if(!item.users.length) {
errors.add(i18n.tr('Не для всех элементов резолюции выбраны ответственные пользователи'));
result.errorCode++;
}
break;
}
}
}
if(result.errorCode != 0) result.errorMessage = [...errors].join('<br>');
return result;
}
async passResolution(){
Cons.showLoader();
try {
const itemsError = this.checkResolutionItems();
if(itemsError.errorCode != 0) throw new Error(itemsError.errorMessage);
const params = this.getParamURL();
params.append("type", 'ACCEPT');
const resultSave = await appAPI.resolution.save(params);
if(resultSave.errorCode != 0) throw new Error(resultSave.errorMessage);
Cons.hideLoader();
showMessage(resultSave.errorMessage, 'success');
UIkit.modal(this.dialog).hide();
} catch (err) {
UIkit.notification.closeAll();
showMessage(i18n.tr(err.message), 'error');
Cons.hideLoader();
}
}
async saveResolution(){
Cons.showLoader();
try {
const itemsError = this.checkResolutionItems();
if(itemsError.errorCode != 0) throw new Error(itemsError.errorMessage);
const params = this.getParamURL();
params.append("type", 'SAVE');
const resultSave = await appAPI.resolution.save(params);
if(resultSave.errorCode != 0) throw new Error(resultSave.errorMessage);
Cons.hideLoader();
showMessage(resultSave.errorMessage, 'success');
UIkit.modal(this.dialog).hide();
} catch (err) {
UIkit.notification.closeAll();
showMessage(i18n.tr(err.message), 'error');
Cons.hideLoader();
}
}
addButtonListener(){
this.buttonAddResolutionItem.on('click', e => {
e.preventDefault();
this.addResolutionItem();
});
this.buttonPassResolution.on('click', e => {
e.preventDefault();
this.passResolution();
});
if(this.resolution.statusID == 0) {
this.buttonSaveResolution.on('click', e => {
e.preventDefault();
this.saveResolution();
});
}
this.isControl.on('change', e => {
this.resolution.controlled = e.target.checked;
});
}
addDialogListener(){
this.dialog.on('shown', () => {
this.addButtonListener();
});
this.dialog.on('hidden', () => {
this.dialog.remove();
});
}
async init() {
if(this._doc.hasOwnProperty('documentID')) this.documentID = this._doc.documentID;
if(this._doc.hasOwnProperty('actionID')) this.actionID = this._doc.actionID;
this.resolution = this._doc.resolutions.find(x => x.authorID == AS.OPTIONS.currentUser.userid);
this.resolution.canEdit = this.resolution.canEdit == 'true' ? true : false;
this.resolution.controlled = this.resolution.controlled == 'true' ? true : false;
const {systemSettings} = Cons.getAppStore();
const dict_resolution_text = await appAPI.getDictionaryByCode('resolution');
this.resolutionText = UTILS.parseDict(dict_resolution_text);
this.workTypes = systemSettings.resolution_work_types;
this.completionForms = systemSettings.work_completion_forms;
this.day_finish_time = systemSettings.day_finish_time.slice(0, 5);
this.completion_date_for_resolution = systemSettings.completion_date_for_resolution == 'true' ? true : false;
this.date_resolution_after_finish_document = systemSettings.date_resolution_after_finish_document == 'true' ? true : false;
this.resolutionText.sort((a,b) => {
const textA = a.text.toUpperCase();
const textB = b.text.toUpperCase();
if (textA < textB) return -1;
if (textA > textB) return 1;
return 0;
});
this.resolution.items.forEach(item => {
this.items[item.itemID] = item;
});
this.dialog = this.getModal();
UIkit.modal(this.dialog).show();
this.addDialogListener();
if(this.resolution.items.length) {
this.resolution.items.forEach(item => {
this.addResolutionItem(item);
});
}
}
}
.workflow-tab-container {
display: flex;
flex-direction: row;
align-items: center;
border-bottom: 1px solid #e5e5e5;
height: 33px;
}
#workflow_add_file_button {
width: 32px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
}
.workflow-context-menu {
position: absolute;
display: none;
background-color: #fff;
box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.2);
padding: 10px 0;
z-index: 100;
}
.workflow-context-menu ul {
list-style: none;
margin: 0;
padding: 0;
}
.workflow-context-menu ul li {
padding: 0;
background-color: #fff;
display: block;
}
.workflow-context-menu ul li a {
color: #555;
padding: 5px 10px;
}
.workflow-context-menu ul li a:hover {
color: var(--ic);
}
.workflow-context-menu ul .uk-disabled a {
color: #dedede;
}
.document-files-tabs li,
.document-files-container li {
list-style: none;
}
.document-files-container .data {
display: none;
width: 100%;
}
.document-files-container .data.active {
display: block;
}
.document-files-container,
.document-files-container ul {
padding: 0;
height: calc(100% - 60px);
overflow: auto;
}
.document-files-tabs {
display: flex;
width: calc(100% - 32px);
margin: 0;
flex-wrap: wrap;
padding: 0;
}
.document-files-tabs .tab a {
display: block;
text-align: center;
padding: 5px 10px;
color: #999;
border-bottom: 1px solid transparent;
text-transform: capitalize;
font-size: 9pt;
transition: color 0.1s ease-in-out;
text-decoration: none;
}
.document-files-tabs .tab a:hover {
color: #666;
}
.document-files-tabs .tab.active a {
color: #333;
border-color: #1e87f0;
}
.document-file-item a {
color: #999;
padding: 5px 0;
display: block;
text-decoration: none;
font-size: .875rem;
text-overflow: ellipsis;
overflow: hidden;
}
.document-file-item a:hover {
color: #666;
}
.document-file-item.select a {
color: #333;
}
const getMenuCoordinates = (menu, pageX, pageY) => {
const menuW = menu.width();
const menuH = menu.height();
const documentW = $(document).width();
const documentH = $(document).height();
const coordinates = {
left: pageX,
top: pageY
};
if((menuW + pageX) > documentW) coordinates.left = (documentW - menuW - 50);
if((menuH + pageY) > documentH) coordinates.top = (documentH - menuH - 50);
return coordinates;
}
this.DocumentAttachments = class {
constructor(documentID, container, notEditable = false){
this.documentID = documentID;
this.container = container;
this.notEditable = notEditable;
this.attachmentFilesCount = 0;
this.workFilesCount = 0;
this.path = "ase:attachmentContainer";
this.init();
}
tabTranslate(){
const tabAttachName = `${i18n.tr("Приложения")} (${this.attachmentFilesCount})`;
const tabWorkName = `${i18n.tr("Прочие")} (${this.workFilesCount})`;
this.tabAttachmentContainer.find('a').text(tabAttachName);
this.tabWorkContainer.find('a').text(tabWorkName);
}
getContextItemMenu(name, hoverColor, icon, handler) {
return $(`<li><a style="--ic: ${hoverColor}" href="#"><span class="uk-margin-small-right" uk-icon="icon: ${icon}"></span>${i18n.tr(name)}</a></li>`)
.on('click', e => {
e.preventDefault();
e.target.blur();
handler();
});
}
getContextMenu(li, item, type) {
const {name, uuid} = item;
const menu = $('<div>', {class: 'workflow-context-menu', style: `min-width: 200px;`});
const ul = $('<ul class="uk-nav-default uk-nav-parent-icon" uk-nav>');
const open = this.getContextItemMenu('Открыть', '#999', 'push', () => {
this.container.trigger({type: 'file_context_open', eventParam: item});
});
const download = this.getContextItemMenu('Скачать', '#999', 'download', () => {
Cons.showLoader();
UTILS.fileDownload(name, uuid);
Cons.hideLoader();
});
ul.append(open, download);
if(!this.notEditable) {
const del = this.getContextItemMenu('Удалить', '#ef2d35', 'trash', () => {
UIkit.modal.confirm(i18n.tr('Вы действительно хотите удалить файл ${fileName}?').replace('${fileName}', `"${name}"`)).then(async () => {
const deleteResult = await appAPI.deleteFile(uuid);
if(deleteResult && deleteResult.errorCode == 0) {
li.remove();
if(type == "ase:attachmentContainer") {
this.attachmentFilesCount--;
} else {
this.workFilesCount--;
}
this.tabTranslate();
} else {
showMessage(deleteResult.errorMessage, 'error');
}
}, () => null);
});
ul.append('<li class="uk-nav-divider"></li>', del);
}
menu.append(ul);
return menu;
}
renderListFiles(files, listFiles){
listFiles.empty();
for(let i = 0; i < files.length; i++) {
const file = files[i];
const li = $(`<li class="uk-position-relative" index-file="${i}" title="${file.name}">${i+1}. ${file.name}</li>`);
const delButton = $(`<a href="#" uk-icon="icon: trash" class="uk-position-center-right uk-hidden-hover"></a>`);
li.append(delButton);
listFiles.append(li);
delButton.on('click', e => {
files.splice(i, 1);
li.remove();
this.renderListFiles(files, listFiles);
});
}
}
async addFileButtonHandler(){
let me = this;
const content = $('<div>');
const listFiles = $('<ul>', {class: 'uk-list uk-list-divider'});
const {container, inputFile} = Components.createSelectFileMultiple(i18n.tr('Выбрать файлы'));
let files = [];
let uploadErrors = 0;
let finishUpload = false;
content.append(container, listFiles);
inputFile.on('change', e => {
files = [...inputFile[0].files];
uploadErrors = 0;
me.renderListFiles(files, listFiles);
});
const dialog = await UTILS.getModalDialog(i18n.tr('Добавить документ'), content, i18n.tr('Загрузить'), async () => {
Cons.showLoader();
try {
if(!files.length) throw new Error('Необходимо прикрепить файл');
dialog.find('.uk-button-primary').hide();
container.hide();
for(let i = 0; i < files.length; i++) {
const file = files[i];
const statusLable = listFiles.find(`[index-file="${i}"]`);
const fileReader = new FileReader();
statusLable.find('a').remove();
if(file.size > 20971520) {
statusLable.append(`<span style="color: orange;"> - ${i18n.tr('Размер файла не должен превышать 20 МБ')}</span>`);
uploadErrors++;
} else {
Cons.showLoader();
const filePath = await appAPI.startUpload();
if(!filePath) {
statusLable.append(`<span style="color: red;"> - ${i18n.tr('Ошибка создания временного файла')}</span>`);
uploadErrors++;
} else {
fileReader.onload = async function (e) {
Cons.showLoader();
const b64encoded = btoa(new Uint8Array(e.target.result).reduce((data, byte) => data + String.fromCharCode(byte), ''));
const data = new FormData();
data.append('body', b64encoded);
const uploadResult = await appAPI.uploadPart(filePath.file, data);
if(!uploadResult) {
statusLable.append(`<span style="color: red;"> - ${i18n.tr('Ошибка загрузки файла')}</span>`);
uploadErrors++;
} else {
if(uploadResult.errorCode != '0') {
statusLable.append(`<span style="color: red;"> - ${i18n.tr(uploadResult.errorMessage)}</span>`);
uploadErrors++;
} else {
Cons.showLoader();
const {errorCode, errorMessage} = await appAPI.addFileToDocument(me.documentID, file.name, filePath.file, me.path) || {};
if(errorCode && errorCode == '0') {
statusLable.append(`<span style="color: green;"> - ${i18n.tr('готово')}</span>`);
} else {
if(errorMessage.indexOf('This node already exists') != -1) {
statusLable.append(`<span style="color: red;"> - ${i18n.tr('Файл ${fileName} уже существует').replace('${fileName}', file.name)}</span>`);
} else {
statusLable.append(`<span style="color: red;"> - ${i18n.tr(errorMessage)}</span>`);
}
uploadErrors++;
}
Cons.hideLoader();
}
}
Cons.hideLoader();
dialog.find('button.uk-modal-close').text('OK');
}
fileReader.readAsArrayBuffer(file);
}
}
}
Cons.hideLoader();
if(uploadErrors > 0) showMessage(i18n.tr('При загрузке файлов возникли ошибки'), 'error');
finishUpload = true;
} catch (err) {
Cons.hideLoader();
showMessage(i18n.tr(err.message), 'error');
}
});
UIkit.modal(dialog).show();
dialog.on('hidden', () => {
dialog.remove();
if(finishUpload) me.init();
});
}
renderApp(){
this.container.empty();
this.rootContainer = $('<div>', {style: "width: 100%; height: 100%;"});
this.tabContainer = $('<div>', {class: "workflow-tab-container"});
this.addFileButton = $('<a href="#" id="workflow_add_file_button" uk-icon="plus"></a>');
this.workflowTabs = $('<ul class="uk-child-width-expand document-files-tabs">');
this.tabAttachmentContainer = $(`<li class="tab active" path="ase:attachmentContainer"><a href="#"></a></li>`);
this.tabWorkContainer = $(`<li class="tab" path="ase:workContainer"><a href="#"></a></li>`);
this.workflowTabs.append(this.tabAttachmentContainer, this.tabWorkContainer);
this.filesContainer = $('<ul>', {class: "uk-margin document-files-container"});
this.filesAttachmentContainer = $('<li class="data uk-padding-small uk-padding-remove-vertical active" path="ase:attachmentContainer"></li>');
this.filesWorkContainer = $('<li class="data uk-padding-small uk-padding-remove-vertical" path="ase:workContainer"></li>');
this.filesContainer.append(this.filesAttachmentContainer, this.filesWorkContainer);
if(this.notEditable) {
this.tabContainer.append(this.workflowTabs);
} else {
this.tabContainer.append(this.addFileButton, this.workflowTabs);
}
this.rootContainer.append(this.tabContainer, this.filesContainer);
this.container.append(this.rootContainer);
this.addFileButton.on('click', e => {
e.preventDefault();
e.target.blur();
this.addFileButtonHandler();
});
this.addListenerTabs();
}
addListenerTabs(){
this.tabAttachmentContainer.find('a').on('click', e => {
e.preventDefault();
e.target.blur();
this.tabAttachmentContainer.addClass('active');
this.filesAttachmentContainer.addClass('active');
this.filesWorkContainer.removeClass('active');
this.tabWorkContainer.removeClass('active');
this.path = "ase:attachmentContainer";
});
this.tabWorkContainer.find('a').on('click', e => {
e.preventDefault();
e.target.blur();
this.tabAttachmentContainer.removeClass('active');
this.filesAttachmentContainer.removeClass('active');
this.filesWorkContainer.addClass('active');
this.tabWorkContainer.addClass('active');
this.path = "ase:workContainer";
});
}
addFileItemListeners(li, item, type) {
const {name, uuid} = item;
let timeoutId;
li.on('click', e => {
timeoutId = setTimeout(() => {
if(!timeoutId) return;
this.filesContainer.find('.document-file-item').removeClass('select');
li.addClass('select');
this.container.trigger({type: 'file_click', eventParam: item});
}, 200);
}).on('dblclick', e => {
clearTimeout(timeoutId);
timeoutId = null;
e.preventDefault();
this.filesContainer.find('.document-file-item').removeClass('select');
li.addClass('select');
this.container.trigger({type: 'file_dbl_click', eventParam: item});
}).on('contextmenu', e => {
$('.workflow-context-menu').remove();
const contextmenu = this.getContextMenu(li, item, type);
$('body').append(contextmenu);
const {left, top} = getMenuCoordinates(contextmenu, event.pageX, event.pageY);
contextmenu.css({
'left': `${left}px`,
'top': `${top}px`
});
contextmenu.show('fast');
return false;
});
}
getFileList(attachment, type) {
const ul = $('<ul style="overflow: hidden;">');
attachment = attachment.sort((a,b) => new Date(a.created) - new Date(b.created));
attachment.forEach((item, i) => {
const {name} = item;
const li = $('<li>', {class: 'document-file-item'});
li.append(`<a href="#" title="${name}" style="text-overflow: ellipsis; overflow: hidden;">${name}</a>`);
if(type == 'ase:attachmentContainer' && i == 0) li.addClass('select');
this.addFileItemListeners(li, item, type);
ul.append(li);
});
return ul;
}
async init(){
const res = await appAPI.getDocumentAttachments(this.documentID);
this.renderApp();
if(res) {
this.attachmentFilesCount = res.attachments.length;
this.workFilesCount = res.work_files.length;
this.filesAttachmentContainer.append(this.getFileList(res.attachments, 'ase:attachmentContainer'));
this.filesWorkContainer.append(this.getFileList(res.work_files));
} else {
this.attachmentFilesCount = 0;
this.workFilesCount = 0;
}
this.tabTranslate();
}
}
.tab-comments-container {
display: flex;
flex-direction: row;
align-items: center;
border-bottom: 1px solid #e5e5e5;
margin: 10px 0;
width: 100%;
}
.comments-container {
padding: 5px 5px 20px 5px;
display: none;
flex-direction: column;
gap: 10px;
overflow-y: auto;
overflow-x: hidden;
width: 100%;
}
.comments-container.active {
display: flex;
}
.comments-container .uk-comment-title {
font-size: 0.9rem;
line-height: 1;
}
.comments-container .uk-comment-meta {
font-size: 0.7em;
line-height: 1;
}
.comments-container .uk-comment-body {
margin-top: 5px !important;
font-size: 0.9rem;
line-height: 1;
}
.comments-container .uk-comment {
position: relative;
border: 1px solid var(--gray);
z-index: 1;
}
.comments-container .uk-comment::after {
content: "";
position: absolute;
display: block;
width: 20px;
height: 20px;
right: 0;
bottom: 0;
border-left: 1px solid var(--gray);
border-top: 1px solid var(--gray);
background: #f8f8f8;
transform: translate(0px, 1px);
z-index: 2;
}
.comments-container .uk-comment::before {
content: "";
position: absolute;
display: block;
width: 20px;
height: 20px;
right: 0;
bottom: 0;
background: var(--panel-bg);
border-left: 1px solid var(--gray);
transform: skewX(-50deg) translate(12px, 1px);
z-index: 3;
}
.add-comment-input::placeholder {
font-size: 12px;
}
#add-comment-button {
width: 32px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
}
.tab-buttons-container {
width: calc(100% - 32px);
display: flex;
flex-wrap: wrap;
}
.comment-tab-name {
font-size: 9pt;
border-bottom: 1px solid transparent;
color: #999;
text-align: center;
margin-left: 5px;
padding: 5px 12px;
cursor: pointer;
transition: .2s;
}
.comment-tab-name:hover {
color: #333;
}
.comment-tab-name.active {
border-bottom: 1px solid #1e87f0;
color: #333;
}
const getModalDialog = async (body, handler) => {
const dialogTitle = i18n.tr("Комментарий");
const exitButtonText = i18n.tr("Закрыть");
const saveButtonText = i18n.tr("Сохранить");
const dialog = $('<div class="uk-flex-top" uk-modal>');
const md = $('<div class="uk-modal-dialog uk-margin-auto-vertical">');
const modalBody = $('<div class="uk-modal-body" uk-overflow-auto>');
const footer = $('<div class="uk-modal-footer uk-text-right">');
modalBody.append(body);
footer.append(`<button class="uk-button uk-button-default uk-modal-close" type="button">${exitButtonText}</button>`);
md.append(`<div class="uk-modal-header"><h3>${dialogTitle}</h3></div>`).append(modalBody).append(footer);
dialog.append(md);
const actionButton = $('<button class="uk-button uk-button-primary uk-margin-left" type="button">');
actionButton.html(saveButtonText).on('click', handler);
footer.append(actionButton);
return dialog;
}
this.DocumentComments = class {
constructor(documentID, workID, container){
this.documentID = documentID;
this.workID = workID;
this.container = container;
this.commentType = 'DOCUMENT'; // WORK, DOCUMENT, PERSONAL
this.init();
}
async getComments(){
return new Promise(async resolve => {
rest.synergyGet(`api/docflow/doc/comments/list?documentID=${this.documentID}&count=100&locale=${AS.OPTIONS.locale}`,
resolve,
err => {
console.log(`ERROR [ getComments ]: ${JSON.stringify(err)}`);
resolve(null);
}
);
});
}
async getWorkComments(type){
return new Promise(async resolve => {
rest.synergyGet(`api/workflow/work/${this.workID}/comments?type=${type}&count=100&locale=${AS.OPTIONS.locale}`, resolve, err => {
console.log(`ERROR [ getWorkComments ]: ${JSON.stringify(err)}`);
resolve(null);
});
});
}
async removeComment(commentID){
return new Promise(async resolve => {
rest.synergyGet(`api/docflow/doc/comments/remove?commentID=${commentID}`, resolve, err => {
console.log(`ERROR [ removeComment ]: ${JSON.stringify(err)}`);
resolve(null);
});
});
}
async removeWorkComment(commentID) {
return new Promise(async resolve => {
rest.synergyGet(`api/workflow/work/comments/remove?commentID=${commentID}`, resolve, err => {
console.log(`ERROR [ removeWorkComment ]: ${JSON.stringify(err)}`);
resolve(null);
});
});
}
async saveComment(param){
const {comment, commentID} = param;
const bodyObject = {documentID: this.documentID, comment: encodeURIComponent(comment)};
if(commentID) bodyObject.commentID = commentID;
return new Promise(async resolve => {
rest.synergyPost(`api/docflow/doc/comments/save?locale=${AS.OPTIONS.locale}`,
bodyObject, "application/x-www-form-urlencoded; charset=utf-8", resolve,
err => {
console.log(`ERROR [ saveComment ]: ${JSON.stringify(err)}`);
resolve(null);
}
);
});
}
async saveWorkComment(param) {
const {comment, commentID} = param;
const bodyObject = {workID: this.workID, type: this.commentType, comment: encodeURIComponent(comment)};
if(commentID) bodyObject.commentID = commentID;
return new Promise(async resolve => {
rest.synergyPost(`api/workflow/work/${this.workID}/comments/save?locale=${AS.OPTIONS.locale}`,
bodyObject, "application/x-www-form-urlencoded; charset=utf-8", resolve,
err => {
console.log(`ERROR [ saveWorkComment ]: ${JSON.stringify(err)}`);
resolve(null);
}
);
});
}
getCommentArticle(data){
const {commentID, comment, author: {name: authorName}, created_label, is_editable, is_deletable} = data;
const article = $('<article>', {class: "uk-comment uk-comment-primary uk-visible-toggle uk-padding-small"});
const header = $('<header>', {class: "uk-comment-header uk-position-relative uk-margin-remove"});
const buttonsBlock = $('<div>', {class: "uk-position-top-right uk-hidden-hover"});
const commentBody = $('<div>', {class: "uk-comment-body uk-margin-small-top"});
if(is_editable == 'true') {
const editCommentButton = $(`<a href="#" uk-icon="icon: pencil"></a>`);
buttonsBlock.append(editCommentButton);
editCommentButton.on('click', async e => {
e.preventDefault();
e.target.blur();
const commentInput = $(`<textarea class="uk-textarea add-comment-input" rows="5"></textarea>`);
commentInput.val(comment);
const dialog = await getModalDialog(commentInput, async () => {
try {
if(commentInput.val() != "") {
Cons.showLoader();
commentInput.removeClass('uk-form-danger');
if(this.workID) {
await this.saveWorkComment({commentID, comment: commentInput.val()});
} else {
await this.saveComment({commentID, comment: commentInput.val()});
}
this.renderComments();
Cons.hideLoader();
UIkit.modal(dialog).hide();
} else {
commentInput.addClass('uk-form-danger');
}
} catch (err) {
Cons.hideLoader();
UIkit.modal(dialog).hide();
showMessage(err.message, 'error');
}
});
UIkit.modal(dialog).show();
dialog.on('hidden', () => {
dialog.remove();
});
});
}
if(is_deletable == 'true') {
const delCommentButton = $(`<a href="#" uk-icon="icon: trash"></a>`);
buttonsBlock.append(delCommentButton);
delCommentButton.on('click', async e => {
e.preventDefault();
e.target.blur();
if(this.workID) {
await this.removeWorkComment(commentID);
} else {
await this.removeComment(commentID);
}
this.renderComments();
});
}
header
.append(`<h4 class="uk-comment-title uk-margin-remove">${authorName || ''}</h4>`)
.append(`<span class="uk-comment-meta uk-margin-remove">${created_label}</span>`)
.append(buttonsBlock);
commentBody.html(comment.replaceAll('\n', '<br>'));
article.append(header).append(commentBody);
return article;
}
async renderComments(){
if(this.workID) {
const workComments = await this.getWorkComments('WORK');
const docComments = await this.getWorkComments('DOCUMENT');
const userComments = await this.getWorkComments('PERSONAL');
this.commentListWork.empty();
this.commentListDoc.empty();
this.commentListUser.empty();
if(workComments && workComments.length > 0) {
workComments.forEach(data => this.commentListWork.append(this.getCommentArticle(data)));
}
if(docComments && docComments.length > 0) {
docComments.forEach(data => this.commentListDoc.append(this.getCommentArticle(data)));
}
if(userComments && userComments.length > 0) {
userComments.forEach(data => this.commentListUser.append(this.getCommentArticle(data)));
}
} else {
const listComments = await this.getComments();
this.commentListDoc.empty();
if(listComments && listComments.length) {
listComments.forEach(data => this.commentListDoc.append(this.getCommentArticle(data)));
}
}
}
renderApp(){
this.container.empty();
const actionPanel = $('<div>', {class: 'tab-comments-container'});
const tabs = $('<div>', {class: 'tab-buttons-container'});
const textareaContainer = $('<div style="padding: 0 5px; width: 100%;">');
this.commentListDoc = $('<div>', {class: 'comments-container'});
this.commentListWork = $('<div>', {class: 'comments-container'});
this.commentListUser = $('<div>', {class: 'comments-container'});
const tabDoc = $(`<span class="comment-tab-name">${i18n.tr("Документ")}</span>`);
const tabWork = $(`<span class="comment-tab-name">${i18n.tr("Работа")}</span>`);
const tabUser = $(`<span class="comment-tab-name">${i18n.tr("Личные")}</span>`);
this.addCommentInput = $('<textarea class="uk-textarea add-comment-input" rows="2"></textarea>')
.attr('placeholder', i18n.tr("Введите комментарий и нажмите Enter..."));
this.addCommentButton = $('<a href="#" id="add-comment-button" uk-icon="plus"></a>');
textareaContainer.append(this.addCommentInput);
actionPanel.append(this.addCommentButton, tabs);
this.container.append(actionPanel, textareaContainer, '<hr style="margin: 10px 0;" />');
if(this.workID) {
this.commentType = 'WORK';
tabWork.addClass('active');
this.commentListWork.addClass('active');
tabs.append(tabWork, tabDoc, tabUser);
tabs.addClass('uk-child-width-expand');
this.container.append(this.commentListWork, this.commentListDoc, this.commentListUser);
} else {
this.commentType = 'DOCUMENT';
tabDoc.addClass('active');
this.commentListDoc.addClass('active');
tabs.append(tabDoc);
this.container.append(this.commentListDoc);
}
tabDoc.on('click', e => {
this.commentType = 'DOCUMENT';
tabWork.removeClass('active');
tabUser.removeClass('active');
tabDoc.addClass('active');
this.commentListWork.removeClass('active');
this.commentListUser.removeClass('active');
this.commentListDoc.addClass('active');
});
tabWork.on('click', e => {
this.commentType = 'WORK';
tabUser.removeClass('active');
tabDoc.removeClass('active');
tabWork.addClass('active');
this.commentListDoc.removeClass('active');
this.commentListUser.removeClass('active');
this.commentListWork.addClass('active');
});
tabUser.on('click', e => {
this.commentType = 'PERSONAL';
tabWork.removeClass('active');
tabDoc.removeClass('active');
tabUser.addClass('active');
this.commentListDoc.removeClass('active');
this.commentListWork.removeClass('active');
this.commentListUser.addClass('active');
});
}
initCommentListiners(){
this.addCommentInput.off().on('keyup', async e => {
if (e.keyCode === 13) {
e.preventDefault();
e.target.blur();
const comment = this.addCommentInput.val().replaceAll('\n', '').trim();
if(comment != '') {
if(this.workID) {
await this.saveWorkComment({comment});
} else {
await this.saveComment({comment});
}
this.addCommentInput.val('');
this.renderComments();
} else {
this.addCommentInput.val('');
}
}
});
this.addCommentButton.off().on('click', async e => {
e.preventDefault();
e.target.blur();
const commentInput = $(`<textarea class="uk-textarea add-comment-input" rows="5"></textarea>`);
const dialog = await getModalDialog(commentInput, async () => {
try {
if(commentInput.val() != "") {
Cons.showLoader();
commentInput.removeClass('uk-form-danger');
if(this.workID) {
await this.saveWorkComment({comment: commentInput.val()});
} else {
await this.saveComment({comment: commentInput.val()});
}
this.renderComments();
Cons.hideLoader();
UIkit.modal(dialog).hide();
} else {
commentInput.addClass('uk-form-danger');
}
} catch (err) {
Cons.hideLoader();
UIkit.modal(dialog).hide();
showMessage(err.message, 'error');
}
});
UIkit.modal(dialog).show();
dialog.on('hidden', () => {
dialog.remove();
});
});
}
async init(){
if(!this.workID && !this.documentID) return;
if(!this.workID && this.documentID) {
const docInfo = await appAPI.getDocumentInfo(this.documentID);
if(docInfo && docInfo.actions.length) this.workID = docInfo.actions[0];
}
if(this.workID && !this.documentID) {
this.documentID = await appAPI.getWorkDocument(this.workID);
}
this.container.off().on('change_locale_after', e => {
this.renderApp();
this.renderComments();
this.initCommentListiners();
});
this.renderApp();
this.renderComments();
this.initCommentListiners();
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment