Commit bf0ed460 authored by Samir Sadyhov's avatar Samir Sadyhov 🤔

Timeline

parent 56a1e571
Перед настройкой компонента необходимо убедиться чтоб были прописаны коды этапов маршрута, данные коды необходимо будет передать в настройки компонента в массив `model.stages`
## Пример настройки компонента:
```js
//Наименование компонента (не обязательно)
model.name = {
ru: 'Этапы работ',
kk: 'Жұмыс кезеңдері',
en: 'Stages of work'
}
//список этапов работ (обязательно)
model.stages = [];
//добавляем этап с наименованием и кодом процесса (маршрута)
model.stages.push({
stageName: {
ru: 'Этап 1',
kk: 'Жұмыс 1',
en: 'Stage 1'
},
processCode: 'process_stage_1'
});
```
<div class="timeline_container">
<div class="timeline_title"></div>
<div class="timeline_content"></div>
</div>
<style>
.timeline_container {
display: flex;
flex-direction: column;
align-items: center;
padding: 24px 10px;
gap: 30px;
position: relative;
border: 1px solid #ACB5BD;
border-radius: 12px;
background: #fff;
overflow: auto;
}
.timeline_title {
font-style: normal;
font-weight: 700;
font-size: 16px;
line-height: 18px;
text-align: center;
color: #212429;
}
.timeline_content {
display: flex;
gap: 198px;
height: 32px;
}
.timeline_step {
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.timeline_step_circle {
position: absolute;
width: 24px;
height: 24px;
background: #B4B4B4;
border: 4px solid #B4B4B4;
border-radius: 50%;
cursor: pointer;
z-index: 2;
}
.timeline_step_line {
position: absolute;
height: 2px;
width: 198px;
background: #B4B4B4;
}
.timeline_step.finished .timeline_step_circle {
background: #FF8C1C;
border: 4px solid #FF8C1C;
}
.timeline_step.active .timeline_step_circle {
background: #FFFFFF;
border: 4px solid #FF8C1C;
}
.timeline_step.finished .timeline_step_line,
.timeline_step.active .timeline_step_line {
background: #FF8C1C;
}
.timeline_step.first .timeline_step_line {
width: 99px;
transform: translate(49px, 0);
}
.timeline_step.last .timeline_step_line {
width: 99px;
transform: translate(-50px, 0);
}
.timeline_message {
position: absolute;
background: #495057;
border-radius: 8px;
z-index: 1000;
padding: 16px 19px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.timeline_message_button_close {
background: #FF8C1C;
border-radius: 8px;
width: 115px;
height: 29px;
border: none;
color: #fff;
cursor: pointer;
font-weight: 500;
font-size: 14px;
margin-top: 17px;
transition: .3s;
}
.timeline_message_button_close:hover {
opacity: 0.9;
}
.timeline_message_item {
color: #F5F5F6;
font-weight: 500;
font-size: 14px;
line-height: 18px;
display: flex;
gap: 5px;
max-width: 400px;
}
.timeline_message_item .timeline_message_item_value {
font-weight: 600;
}
.timeline_message::before {
content: '';
disply: block;
width: 24px;
height: 24px;
background: #495057;
position: absolute;
top: -12px;
left: 36px;
transform: rotate(45deg);
}
</style>
/* Настройки на компоненте:
//Наименование компонента (не обязательно)
model.name = {
ru: 'Этапы работ',
kk: 'Жұмыс кезеңдері',
en: 'Stages of work'
}
//список этапов работ (обязательно)
model.stages = [];
//добавляем этап с наименованием и кодом процесса (маршрута)
model.stages.push({
stageName: {
ru: 'Этап 1',
kk: 'Жұмыс 1',
en: 'Stage 1'
},
processCode: 'process_stage_1'
});
*/
class Timeline {
constructor(parentContainer) {
this.parentContainer = parentContainer;
this.timelineContainer = parentContainer.find('.timeline_container');
this.timelineTitle = parentContainer.find('.timeline_title');
this.timelineContent = parentContainer.find('.timeline_content');
this.active = false;
this.init();
}
getCoords(elem){
const box = $(elem)[0].getBoundingClientRect();
const formOffset = view.playerView.container.offset();
return {
top: box.bottom - formOffset.top + (box.height / 2),
left: box.left - formOffset.left - box.width
};
}
getStep(item){
const step = $('<div>', {class: 'timeline_step'});
const circle = $('<div>', {class: 'timeline_step_circle'});
const line = $('<div>', {class: 'timeline_step_line'});
step.append(circle, line);
if(item) {
const {processCode} = item;
const finishedProcess = this.getFinishedProcesses(this.processes, processCode);
const notFinishedProcess = this.getNotFinishedProcesses(this.processes, processCode);
if(finishedProcess.length && notFinishedProcess.length) {
step.addClass('active');
this.active = true;
} else if(notFinishedProcess.length) {
step.addClass('active');
this.active = true;
} else if (finishedProcess.length && !this.active) {
step.addClass('finished');
}
}
return step;
}
createMessage(elem, data) {
$('.timeline_message').fadeOut(300, function(){
$(this).remove();
});
const coords = this.getCoords(elem);
const messageBox = $('<div>', {class: 'timeline_message'});
const content = $('<div>', {class: 'timeline_message_content'});
const closeButton = $('<button>', {class: 'timeline_message_button_close'});
messageBox.css({
'left': `${coords.left}px`,
'top': `${coords.top}px`
});
data.forEach(item => {
const {title, value} = item;
const msg = $('<div>', {class: 'timeline_message_item'});
msg.append(
`<span class="timeline_message_item_title">${title}</span>`,
`<span> - </span>`,
`<span class="timeline_message_item_value">${value}</span>`
)
content.append(msg);
});
closeButton.text(i18n.tr('Понятно'));
closeButton.on('click', e => {
messageBox.fadeOut(300, function(){
$(this).remove();
});
});
messageBox.append(content, closeButton);
view.playerView.container.append(messageBox);
}
initStepListener(step, item, title){
const circle = step.find('.timeline_step_circle');
if(item) {
const {stageName, processCode} = item;
const finishedProcess = this.getFinishedProcesses(this.processes, processCode);
const notFinishedProcess = this.getNotFinishedProcesses(this.processes, processCode);
const msgData = [];
msgData.push({title: 'Наименование этапа', value: stageName[AS.OPTIONS.locale]});
if(finishedProcess.length && !this.active) {
msgData.push({title: 'Ответственный', value: finishedProcess[0].responsibleUserName || ''});
msgData.push({title: 'Дата начала этапа', value: finishedProcess[0].started || ''});
msgData.push({title: 'Дата окончания этапа', value: finishedProcess[0].finished || ''});
msgData.push({title: 'Завершил этап', value: finishedProcess[0].finishedUser || ''});
} else if (notFinishedProcess.length) {
msgData.push({title: 'Ответственный', value: notFinishedProcess[0].responsibleUserName || ''});
msgData.push({title: 'Дата начала этапа', value: notFinishedProcess[0].started || ''});
}
circle.attr('title', stageName[AS.OPTIONS.locale]);
circle.on('click', e => {
this.createMessage(circle, msgData);
});
} else {
if(title) circle.attr('title', title);
}
return;
}
render(){
try {
const startStage = this.getStep();
const finishStage = this.getStep();
startStage.addClass('first');
finishStage.addClass('last');
this.initStepListener(startStage, null, i18n.tr('Начало маршрута'));
this.initStepListener(finishStage, null, i18n.tr('Конец маршрута'));
if(this.processes.length) {
startStage.addClass('finished');
if(this.processesIsFinish(this.processes)) finishStage.addClass('finished');
}
this.timelineContent.append(startStage); // Начало маршрута
// все остальные этапы
this.stages.forEach(item => {
const step = this.getStep(item);
this.timelineContent.append(step);
this.initStepListener(step, item);
});
this.timelineContent.append(finishStage); // Конец маршрута
} catch (err) {
console.log(`ERROR Timeline render`, err);
}
}
getNotFinishedProcesses(processes, code) {
const result = [];
const search = p => {
p.forEach(x => {
if (x.typeID == 'ASSIGNMENT_ITEM' && x.started && !x.finished && x.code == code) result.push(x);
if (x.subProcesses.length > 0) search(x.subProcesses);
});
}
search(processes);
return result.sort((a,b) => new Date(b.started) - new Date(a.started));
}
getFinishedProcesses(processes, code) {
const result = [];
const search = p => {
p.forEach(x => {
if (x.typeID == 'ASSIGNMENT_ITEM' && x.finished && x.code == code) result.push(x);
if (x.subProcesses.length > 0) search(x.subProcesses);
});
}
search(processes);
return result.sort((a,b) => new Date(b.finished) - new Date(a.finished));
}
processesIsFinish(processes) {
let started = 0;
let finished = 0;
const search = p => {
p.forEach(x => {
started++;
if (x.started && x.finished) finished++;
if (x.subProcesses.length > 0) search(x.subProcesses);
});
}
search(processes);
return started == finished;
}
async init(){
AS.SERVICES.showWaitWindow();
$('.timeline_message').remove();
try {
const {name = {}, stages, playerModel: {asfDataId}} = model;
this.timelineTitle.text(name[AS.OPTIONS.locale] || 'Этапы проведения работ');
this.stages = stages;
const documentID = await AS.FORMS.ApiUtils.getDocumentIdentifier(asfDataId);
this.processes = await AS.FORMS.ApiUtils.simpleAsyncGet(`rest/api/workflow/get_execution_process?documentID=${documentID}&locale=${AS.OPTIONS.locale}`);
this.render();
AS.SERVICES.hideWaitWindow();
} catch (err) {
AS.SERVICES.hideWaitWindow();
AS.SERVICES.showErrorMessage(err.message);
this.parentContainer.fadeOut();
console.log(`ERROR Timeline init`, err);
}
}
}
new Timeline(view.container);
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