Commit 0277374d authored by doc@bgerp.org's avatar doc@bgerp.org

p11416 Blow cut, paste, free context menu items.

parent d72a7aeb
A: Плагин Blow, пункты меню: "Вырезать", "Вставить", "Отделить", https://bgerp.ru/doc/3.0/manual/plugin/blow/index.html#using
......@@ -47,6 +47,7 @@ blow:board.1.stringExpressionCell=<<END
END
----
[[using]]
== Работа
Blow план отображает иерархию, порядок выполнения и исполнителей процессов.
Форма представления табличная, столбцы таблицы - исполнители либо группы решения.
......@@ -76,21 +77,23 @@ image::_res/board.png[width="800px"]
Например, у задачи может быть процессы-предки *Компонент* и *Проект* одновременно, отображаемые на разных планах для разработчиков и PSO.
Процессы разделяются по различным планам с помощью фильтров по типам, группам в очереди процессов а также с использованеим <<../../kernel/setup.adoc#user-isolation, изоляции>>.
Процессы, содержащие дочерние, выделяются полужирным шрифтом.
Процессы-контейнеры выделяются полужирным шрифтом.
В нижней области таблицы отображается виртуальный родительский процесс *НЕ РАСПРЕДЕЛЁННЫЕ*.
Ячейки с процессами можно перетаскивать мышью, изменяя предка.
Для преобразования процесса в независимый в качестве предка использовать *НЕ РАСПРЕДЕЛЁННЫЕ*.
Для преобразования процесса в независимый в качестве предка использовать *НЕ РАСПРЕДЕЛЁННЫЕ*, либо вызвать <<rc-menu, контекстное меню>>.
При наведении мыши на процесс выделяется он сам и все его дочерние процессы.
Если процесс имеет нулевой приоритет - его приоритет наследуется от родительского процесса.
Контейнерные процессы агрегируют свойства дочерних: исполнителей, бюджеты, затраченное время.
Правым кликом мыши по диаграмме вызывается контекстное меню, позволяющее создавать процесс, идентичный выбранному.
С таким же типом и в том же контейнерном процессе.
[[rc-menu]]
Правым кликом мыши по диаграмме вызывается контекстное меню, позволяющее:
[square]
* создавать процесс, идентичный выбранному: c таким же типом и в том же контейнерном процессе;
* вырезать и вставлять процессы;
* отделять процессы из контейнерных в независимые.
Основная идея Blow диаграммы состоит в постепенном всплывании процессов наверх с распределением их среди исполнителей.
Процессы создаются и структурируются по группам внизу и поднимаются вверх для исполнения.
Контейнерные процессы агрегируют свойства дочерних: бюджеты, затраченное время.
Процессы создаются и структурируются по исполнителям внизу и поднимаются вверх для исполнения.
== Фильтры
Фильтры представляют из себя программируемые кнопки, отображаемые над таблицей плана.
......
......@@ -5,8 +5,8 @@
<c:if test="${not empty board}">
<c:set var="uiid" value="${u:uiid()}"/>
<%@ include file="filters.jsp"%>
<%@ include file="filters.jsp"%>
<table id="${uiid}" class="data" style="width: 100%;">
<tr class="head">
<c:forEach var="column" items="${board.executors}">
......@@ -20,15 +20,18 @@
<c:set var="uiidRcMenu" value="${u:uiid()}"/>
<ul style="display: none; z-index: 2000;" id="${uiidRcMenu}">
<li id="create"><a>${l.l('Новый процесс')}</a></li>
<li id="cut"><a>${l.l('Вырезать')}</a></li>
<li id="paste" style="display: none;"><a>${l.l('Вставить')}</a></li>
<li id="free" style="display: none;"><a>${l.l('Отделить')}</a></li>
</ul>
<script>
$(function () {
bgerp.blow.initTable($('#${uiid}'), $('#${uiidRcMenu}'));
$('#content > #blow-board').data('onShow', function () {
openUrlContent("/user/plugin/blow/board.do?action=show&id=" + ${form.id});
});
$('#content > #blow-board').data('onShow', function () {
openUrlContent("/user/plugin/blow/board.do?action=show&id=" + ${form.id});
});
})
</script>
</c:if>
<%@ page contentType="text/html; charset=UTF-8"%>
#blow-board tr.head td {
width: 1%;
width: 1%;
}
#blow-board td.selected {
background-color: #A9F5F2;
background-color: #A9F5F2;
}
#blow-board table.data > tbody > tr > td.group-border-b {
border-bottom-width: 2px;
border-bottom-color: black;
border-bottom-width: 2px;
border-bottom-color: black;
}
#blow-board table.data > tbody > tr > td.group-border-t {
border-top-width: 2px;
border-top-color: black;
border-top-width: 2px;
border-top-color: black;
}
#blow-board table.data > tbody > tr > td.group-border-l {
border-left-width: 2px;
border-left-color: black;
border-left-width: 2px;
border-left-color: black;
}
#blow-board table.data > tbody > tr > td.group-border-r {
border-right-width: 2px;
border-right-color: black;
border-right-width: 2px;
border-right-color: black;
}
/* http://stripesgenerator.com 5 15 */
#blow-board table.data > tbody > tr > td.cut {
background-image: linear-gradient(45deg, #0b67a1 12.50%, #ffffff 12.50%, #ffffff 50%, #0b67a1 50%, #0b67a1 62.50%, #ffffff 62.50%, #ffffff 100%);
background-size: 28.28px 28.28px;
}
.bgcolor-9AD78A {
background-color: #9AD78A;
background-color: #9AD78A;
}
.bgcolor-FFF1A4 {
background-color: #FFF1A4;
}
background-color: #FFF1A4;
}
.bgcolor-FFBE7E {
background-color: #FFBE7E;
background-color: #FFBE7E;
}
.bgcolor-FD7D89 {
background-color: #FD7D89;
background-color: #FD7D89;
}
......@@ -5,23 +5,23 @@
bgerp.blow = new function() {
const debug = bgerp.debug("blow");
const ATTR_BG_ID = "bg-id";
const ATTR_BG_PARENT_ID = "bg-parent-id";
const ATTR_BG_TYPE_ID = "bg-type-id";
const CLASS_BORDER_TOP = "group-border-t";
const CLASS_BORDER_LEFT = "group-border-l";
const CLASS_BORDER_BOTTOM = "group-border-b";
const CLASS_BORDER_RIGHT = "group-border-r";
const CLASS_SELECTED = "selected";
const CLASS_SELECTED_FILTER = "selected";
const selectItem = ($td, $cells) => {
const itemId = $td.attr(ATTR_BG_ID);
const parentId = $td.attr(ATTR_BG_PARENT_ID);
if (itemId) {
$td.addClass(CLASS_SELECTED);
$cells.filter("[" + ATTR_BG_PARENT_ID + "=" + itemId + "]").each(function () {
......@@ -29,98 +29,122 @@ bgerp.blow = new function() {
});
}
};
const initTable = ($table, $menu) => {
const $cells = $table.find("> tbody > tr:gt(0) > td");
const $cells = $table.find("> tbody > tr:gt(0) > td");
$cells.each(function () {
const $td = $(this);
$td.mouseover(function () {
debug("mouseover", $td);
selectItem($td, $cells);
});
$td.mouseleave(function () {
debug("mouseleave", $td);
$cells.removeClass(CLASS_SELECTED);
});
if ($menu)
bgerp.blow.drag.init($td);
groupBorder($cells, $td);
});
if ($menu)
initRcMenu($table, $menu);
};
const initRcMenu = ($table, $menu) => {
const menu = $menu.menu();
// context of process for popup menu show
let $td;
let $tdCut;
$menu.find("#create").on("click", () => {
let url = null;
if ($td.attr(ATTR_BG_PARENT_ID) > 0)
url = "/user/process.do?action=linkProcessCreate&objectType=processMade&typeId=" + $td.attr(ATTR_BG_TYPE_ID) + "&id=" + $td.attr(ATTR_BG_PARENT_ID);
else
url = "/user/process.do?action=processCreate&typeId=" + $td.attr(ATTR_BG_TYPE_ID);
bgerp.ajax
.post(url)
.done((resp) => {
bgerp.process.open(resp.data.process.id);
});
});
const $cut = $menu.find("#cut");
$cut.on("click", () => {
$table.find("td").removeClass("cut");
$td.addClass("cut");
$paste.show();
$tdCut = $td;
});
const $paste = $menu.find("#paste");
$paste.on("click", () => {
move($tdCut, $td);
});
const $free = $menu.find("#free");
$free.on("click", () => {
move($td, $());
});
$table.on("contextmenu", "td", function (e) {
debug("contextmenu", e);
const $td = $(e.target);
const typeId = $td.attr(ATTR_BG_TYPE_ID);
if (typeId) {
const parentId = $td.attr(ATTR_BG_PARENT_ID);
menu.show().position({
debug("contextmenu", e);
$td = $(e.target);
if ($td.attr(ATTR_BG_TYPE_ID)) {
menu.show().position({
my: "left top",
at: "left bottom",
of: e
});
$(document).one("click", () => {
$(document).one("click", () => {
menu.hide();
});
$menu.find("#create").click((e) => {
let url = null;
if (parentId > 0)
url = "/user/process.do?action=linkProcessCreate&objectType=processMade&typeId=" + typeId + "&id=" + parentId;
else
url = "/user/process.do?action=processCreate&typeId=" + typeId;
bgerp.ajax
.post(url)
.done((resp) => {
bgerp.process.open(resp.data.process.id);
});
});
}
return false;
});
$cut.toggle($td.attr(ATTR_BG_PARENT_ID) !== '');
$paste.toggle(!!$tdCut && $tdCut.attr(ATTR_BG_ID) > 0);
$free.toggle($td.attr(ATTR_BG_PARENT_ID) > 0);
}
return false;
});
};
const groupBorder = ($cells, $td) => {
const itemId = $td.attr(ATTR_BG_ID);
const $children = $cells.filter("[" + ATTR_BG_PARENT_ID + "=" + itemId + "]");
if (itemId == 0)
if (itemId == 0)
$td.addClass(CLASS_BORDER_TOP);
else if (itemId > 0) {
// group of cells
if ($children.length > 0) {
const $root = $td;
debug($root, $children);
$.merge($root, $children).each((i, td) => {
$td = $(td);
const $tr = $td.closest("tr");
if ($tr.is(":nth-child(2)") || $td[0] === $root[0])
$td.addClass(CLASS_BORDER_TOP);
if ($tr.is(":last-child"))
$td.addClass(CLASS_BORDER_BOTTOM);
if ($td.is(":first-child") || $td.prev().attr(ATTR_BG_PARENT_ID) != itemId)
$td.addClass(CLASS_BORDER_LEFT);
if ($td.is(":last-child") || $td.next().attr(ATTR_BG_PARENT_ID) != itemId)
$td.addClass(CLASS_BORDER_RIGHT);
});
......@@ -129,42 +153,42 @@ bgerp.blow = new function() {
else if ($td.attr(ATTR_BG_PARENT_ID) == 0) {
const $tr = $td.closest("tr");
if ($tr.index() > 1 && getCellInSameColumn($tr.prev(), $td).attr(ATTR_BG_PARENT_ID) > 0)
$td.addClass(CLASS_BORDER_TOP);
$td.addClass(CLASS_BORDER_TOP);
}
}
}
// empty cell
else if (!itemId) {
if (!$td.is(":first-child") && isGroupMember($td.prev()))
$td.addClass(CLASS_BORDER_LEFT);
if (!$td.is(":last-child") && isGroupMember($td.next()))
$td.addClass(CLASS_BORDER_RIGHT);
const $tr = $td.closest("tr");
if (!$tr.is(":nth-child(2)") && neighborCellNeedBorder($tr.prev(), $td))
$td.addClass(CLASS_BORDER_TOP);
if (!$tr.is(":last-child") && neighborCellNeedBorder($tr.next(), $td))
$td.addClass(CLASS_BORDER_BOTTOM);
}
};
const neighborCellNeedBorder = ($tr, $td) => {
return isGroupMember(getCellInSameColumn($tr, $td));
};
const getCellInSameColumn = ($tr, $td) => {
const index = $td.index();
const $children = $tr.children();
debug("getCellInSameColumn", $tr, $children);
return $children.length === 1 ? $children.first() : $($children.get(index));
};
};
const isGroupMember = ($td) => {
return $td.attr(ATTR_BG_PARENT_ID) === "" || $td.attr(ATTR_BG_PARENT_ID) > 0;
};
const toggleFilterHighlight = ($table, $button) => {
const $cells = $table.find("td.filter-" + $button.attr(ATTR_BG_ID));
if ($button.toggleClass(CLASS_SELECTED_FILTER).hasClass(CLASS_SELECTED_FILTER))
......@@ -172,72 +196,75 @@ bgerp.blow = new function() {
else
$cells.css("background-color", "");
};
const move = ($td, $tdTo) => {
const targetProcessId = $tdTo.attr(ATTR_BG_ID);
const targetParentProcessId = $tdTo.attr(ATTR_BG_PARENT_ID);
bgerp.ajax
.post("/user/plugin/blow/board.do?action=move&processId=" + $td.attr(ATTR_BG_ID) + "&fromParentProcessId=" + $td.attr(ATTR_BG_PARENT_ID) +
"&parentProcessId=" + (targetParentProcessId > 0 ? targetParentProcessId : targetProcessId))
.done(() => bgerp.shell.contentLoad("/user/blow/board"));
};
// drag & drop
this.drag = new function () {
let $cells = null;
let $tdDrag;
let $cells;
const dragStart = function (e) {
this.style.opacity = '0.4';
e.originalEvent.dataTransfer.setData("text", $(this).attr(ATTR_BG_ID));
this.style.opacity = '0.4';
$tdDrag = $(this);
$cells = $(this).closest("table").find("td");
};
const dragEnd = function (e) {
this.style.opacity = '';
$cells.removeClass(CLASS_SELECTED);
};
const dragOver = (e) => {
debug("drag over", e);
$cells.removeClass(CLASS_SELECTED);
const $td = $(e.target);
if ($td.prop("tagName") === "TD") {
if ($td.prop("tagName") === "TD") {
const targetProcessId = $td.attr(ATTR_BG_ID);
const targetParentProcessId = $td.attr(ATTR_BG_PARENT_ID);
let $root = null;
if (targetParentProcessId > 0)
$root = $cells.filter("td[" + ATTR_BG_ID + "=" + targetParentProcessId + "]");
else if (targetParentProcessId === "")
$root = $td;
if ($root)
selectItem($root, $cells);
}
}
e.preventDefault();
}
const dragDrop = function (e) {
debug("drag drop", e);
e.preventDefault();
const processId = e.originalEvent.dataTransfer.getData("text");
const $td = $(e.target).closest("table").find("td[" + ATTR_BG_ID + "=" + processId + "]");
const targetProcessId = $(this).attr(ATTR_BG_ID);
const targetParentProcessId = $(this).attr(ATTR_BG_PARENT_ID);
bgerp.ajax
.post("/user/plugin/blow/board.do?action=move&processId=" + processId + "&fromParentProcessId=" + $td.attr(ATTR_BG_PARENT_ID) +
"&parentProcessId=" + (targetParentProcessId > 0 ? targetParentProcessId : targetProcessId))
.done(() => bgerp.shell.contentLoad("/user/blow/board"));
move($tdDrag, $(this));
return false;
}
const initDD = ($td) => {
$td.on('dragstart', dragStart);
$td.on('dragend', dragEnd);
$td.on('dragover', dragOver);
$td.on('drop', dragDrop);
};
this.init = initDD;
}
// public functions
this.initTable = initTable;
this.toggleFilterHighlight = toggleFilterHighlight;
......
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