first changes
gitea/salix/1625-worker_department_treeview This commit looks good Details

This commit is contained in:
Joan Sanchez 2019-10-01 15:09:55 +02:00
parent daeb4bed09
commit 183d770483
11 changed files with 372 additions and 83 deletions

View File

@ -0,0 +1,73 @@
ALTER TABLE `vn2008`.`department`
ADD COLUMN `parentFk` INT UNSIGNED NULL AFTER `sons`,
ADD COLUMN `path` VARCHAR(255) NULL AFTER `parentFk`,
CHANGE COLUMN `sons` `sons` DECIMAL(10,0) NOT NULL DEFAULT '0' ;
USE `vn`;
CREATE
OR REPLACE ALGORITHM = UNDEFINED
DEFINER = `root`@`%`
SQL SECURITY DEFINER
VIEW `department` AS
SELECT
`b`.`department_id` AS `id`,
`b`.`name` AS `name`,
`b`.`production` AS `isProduction`,
`b`.`parentFk` AS `parentFk`,
`b`.`path` AS `path`,
`b`.`lft` AS `lft`,
`b`.`rgt` AS `rgt`,
`b`.`isSelected` AS `isSelected`,
`b`.`depth` AS `depth`,
`b`.`sons` AS `sons`
FROM
`vn2008`.`department` `b`;
DROP TRIGGER IF EXISTS `vn2008`.`department_AFTER_DELETE`;
DELIMITER $$
USE `vn2008`$$
CREATE DEFINER = CURRENT_USER TRIGGER `vn2008`.`department_AFTER_DELETE`
AFTER DELETE ON `department` FOR EACH ROW
BEGIN
UPDATE vn.department_recalc SET isChanged = TRUE;
END$$
DELIMITER ;
DROP TRIGGER IF EXISTS `vn2008`.`department_BEFORE_INSERT`;
DELIMITER $$
USE `vn2008`$$
CREATE DEFINER = CURRENT_USER TRIGGER `vn2008`.`department_BEFORE_INSERT`
BEFORE INSERT ON `department` FOR EACH ROW
BEGIN
UPDATE vn.department_recalc SET isChanged = TRUE;
END$$
DELIMITER ;
DROP TRIGGER IF EXISTS `vn2008`.`department_AFTER_UPDATE`;
DELIMITER $$
USE `vn2008`$$
CREATE DEFINER = CURRENT_USER TRIGGER `vn2008`.`department_AFTER_UPDATE`
AFTER UPDATE ON `department` FOR EACH ROW
BEGIN
IF !(OLD.parentFk <=> NEW.parentFk) THEN
UPDATE vn.department_recalc SET isChanged = TRUE;
END IF;
END$$
DELIMITER ;
CREATE TABLE `vn`.`department_recalc` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`isChanged` TINYINT(4) NOT NULL,
PRIMARY KEY (`id`));
INSERT INTO `vn`.`department_recalc` (`id`, `isChanged`) VALUES ('1', '0');
ALTER TABLE `vn2008`.`department`
CHANGE COLUMN `lft` `lft` INT(11) NULL ,
CHANGE COLUMN `rgt` `rgt` INT(11) NULL ;
UPDATE vn.department SET lft = NULL, rgt = NULL;

View File

@ -0,0 +1,37 @@
USE `vn`;
DROP procedure IF EXISTS `department_calcTree`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `department_calcTree`()
BEGIN
/**
* Calculates the #path, #lft, #rgt, #sons and #depth columns of
* the #department table. To build the tree, it uses the #parentFk
* column.
*/
DECLARE vIndex INT DEFAULT 0;
DECLARE vSons INT;
DROP TEMPORARY TABLE IF EXISTS tNestedTree;
CREATE TEMPORARY TABLE tNestedTree
SELECT id, path, lft, rgt, depth, sons
FROM department LIMIT 0;
SET max_sp_recursion_depth = 5;
CALL department_calcTreeRec(NULL, '/', 0, vIndex, vSons);
SET max_sp_recursion_depth = 0;
UPDATE department z
JOIN tNestedTree t ON t.id = z.id
SET z.path = t.path,
z.lft = t.lft,
z.rgt = t.rgt,
z.depth = t.depth,
z.sons = t.sons;
DROP TEMPORARY TABLE tNestedTree;
END$$
DELIMITER ;

View File

@ -0,0 +1,75 @@
USE `vn`;
DROP procedure IF EXISTS `department_calcTreeRec`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `department_calcTreeRec`(
vSelf INT,
vPath VARCHAR(255),
vDepth INT,
INOUT vIndex INT,
OUT vSons INT
)
BEGIN
/**
* Calculates and sets the #path, #lft, #rgt, #sons and #depth
* columns for all children of the passed node. Once calculated
* the last node rgt index and the number of sons are returned.
* To update it's children, this procedure calls itself recursively
* for each one.
*
* @vSelf The node identifier
* @vPath The initial path
* @vDepth The initial depth
* @vIndex The initial lft index
* @vSons The number of direct sons
*/
DECLARE vChildFk INT;
DECLARE vLft INT;
DECLARE vMySons INT;
DECLARE vDone BOOL;
DECLARE vChildren CURSOR FOR
SELECT id FROM department
WHERE (vSelf IS NULL AND parentFk IS NULL)
OR (vSelf IS NOT NULL AND parentFk = vSelf);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
SET vSons = 0;
OPEN vChildren;
myLoop: LOOP
SET vDone = FALSE;
FETCH vChildren INTO vChildFk;
IF vDone THEN
LEAVE myLoop;
END IF;
SET vIndex = vIndex + 1;
SET vLft = vIndex;
SET vSons = vSons + 1;
CALL department_calcTreeRec(
vChildFk,
CONCAT(vPath, vChildFk, '/'),
vDepth + 1,
vIndex,
vMySons
);
SET vIndex = vIndex + 1;
INSERT INTO tNestedTree
SET id = vChildFk,
path = vPath,
lft = vLft,
rgt = vIndex,
depth = vDepth,
sons = vMySons;
END LOOP;
CLOSE vChildren;
END$$
DELIMITER ;

View File

@ -0,0 +1,35 @@
USE `vn`;
DROP procedure IF EXISTS `department_doCalc`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `department_doCalc`()
proc: BEGIN
/**
* Recalculates the department tree.
*/
DECLARE vIsChanged BOOL;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
DO RELEASE_LOCK('vn.department_doCalc');
RESIGNAL;
END;
IF !GET_LOCK('vn.department_doCalc', 0) THEN
LEAVE proc;
END IF;
SELECT isChanged INTO vIsChanged
FROM department_recalc;
IF vIsChanged THEN
UPDATE department_recalc SET isChanged = FALSE;
CALL vn.department_calcTree;
END IF;
DO RELEASE_LOCK('vn.department_doCalc');
END$$
DELIMITER ;

View File

@ -0,0 +1,84 @@
USE `vn`;
DROP procedure IF EXISTS `department_getLeaves`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `department_getLeaves`(
vParentFk INT,
vSearch VARCHAR(255)
)
BEGIN
DECLARE vIsNumber BOOL;
DECLARE vIsSearch BOOL DEFAULT vSearch IS NOT NULL AND vSearch != '';
DROP TEMPORARY TABLE IF EXISTS tNodes;
CREATE TEMPORARY TABLE tNodes
(UNIQUE (id))
ENGINE = MEMORY
SELECT id FROM department LIMIT 0;
IF vIsSearch THEN
SET vIsNumber = vSearch REGEXP '^[0-9]+$';
INSERT INTO tNodes
SELECT id FROM department
WHERE (vIsNumber AND `name` = vSearch)
OR (!vIsNumber AND `name` LIKE CONCAT('%', vSearch, '%'))
LIMIT 1000;
END IF;
IF vParentFk IS NULL THEN
DROP TEMPORARY TABLE IF EXISTS tChilds;
CREATE TEMPORARY TABLE tChilds
ENGINE = MEMORY
SELECT id FROM tNodes;
DROP TEMPORARY TABLE IF EXISTS tParents;
CREATE TEMPORARY TABLE tParents
ENGINE = MEMORY
SELECT id FROM department LIMIT 0;
myLoop: LOOP
DELETE FROM tParents;
INSERT INTO tParents
SELECT parentFk id
FROM department g
JOIN tChilds c ON c.id = g.id
WHERE g.parentFk IS NOT NULL;
INSERT IGNORE INTO tNodes
SELECT id FROM tParents;
IF ROW_COUNT() = 0 THEN
LEAVE myLoop;
END IF;
DELETE FROM tChilds;
INSERT INTO tChilds
SELECT id FROM tParents;
END LOOP;
DROP TEMPORARY TABLE
tChilds,
tParents;
END IF;
IF !vIsSearch THEN
INSERT IGNORE INTO tNodes
SELECT id FROM department
WHERE parentFk <=> vParentFk;
END IF;
SELECT d.id,
d.`name`,
d.parentFk,
d.sons
FROM department d
JOIN tNodes n ON n.id = d.id
ORDER BY depth, `name`;
DROP TEMPORARY TABLE tNodes;
END$$
DELIMITER ;

View File

@ -14,7 +14,8 @@
icon="keyboard_arrow_down"
translate-attr="{title: 'Toggle'}">
</vn-icon>
<vn-check
<section class="tplItem"></section>
<!-- <vn-check
vn-acl="{{$ctrl.aclRole}}"
ng-if="$ctrl.selectable"
field="item.selected"
@ -29,9 +30,9 @@
ng-click="$ctrl.onIconClick(icon, item, $ctrl.parent, $parent.$index)"
vn-acl="{{$ctrl.aclRole}}"
vn-acl-action="remove">
</vn-icon-button>
</vn-icon-button> -->
</div>
<vn-treeview-child
<!-- <vn-treeview-child
items="item.childs"
parent="item"
selectable="$ctrl.selectable"
@ -42,9 +43,9 @@
icons="::$ctrl.icons"
parent-scope="::$ctrl.parentScope"
acl-role="$ctrl.aclRole">
</vn-treeview-child>
</vn-treeview-child> -->
</li>
<li
<!-- <li
ng-if="$ctrl.isInsertable && $ctrl.editable"
ng-click="$ctrl.onCreate($ctrl.parent)"
vn-acl="{{$ctrl.aclRole}}"
@ -57,5 +58,5 @@
Create new one
</div>
</div>
</li>
</li> -->
</ul>

View File

@ -2,9 +2,14 @@ import ngModule from '../../module';
import Component from '../../lib/component';
class Controller extends Component {
constructor($element, $scope) {
constructor($element, $scope, $transclude) {
super($element, $scope);
this.$scope = $scope;
this.$transclude = $transclude;
$transclude($scope.$parent, tClone => {
this.element.querySelector('.tplItem').appendChild(tClone[0]);
}, null, 'tplItem');
}
toggle(event, item) {
@ -35,6 +40,8 @@ class Controller extends Component {
}
}
Controller.$inject = ['$element', '$scope', '$transclude'];
ngModule.component('vnTreeviewChild', {
template: require('./child.html'),
controller: Controller,
@ -50,6 +57,9 @@ ngModule.component('vnTreeviewChild', {
aclRole: '<?',
parentScope: '<'
},
transclude: {
tplItem: '?tplItem'
},
require: {
treeview: '^vnTreeview'
}

View File

@ -8,10 +8,18 @@ import './style.scss';
* @property {String} position The relative position to the parent
*/
export default class Treeview extends Component {
constructor($element, $scope) {
constructor($element, $scope, $transclude) {
super($element, $scope);
this.$scope = $scope;
this.$transclude = $transclude;
this.data = [];
$transclude($scope.$parent, tClone => {
this.element.querySelector('vn-treeview-child').appendChild(tClone[0]);
// Object.assign(scope, option);
// li.appendChild(clone[0]);
// this.scopes[i] = scope;
}, null, 'tplItem');
}
$onInit() {
@ -76,7 +84,7 @@ export default class Treeview extends Component {
}
}
Treeview.$inject = ['$element', '$scope'];
Treeview.$inject = ['$element', '$scope', '$transclude'];
ngModule.component('vnTreeview', {
template: require('./index.html'),
@ -90,5 +98,8 @@ ngModule.component('vnTreeview', {
draggable: '<?',
droppable: '<?',
aclRole: '@?'
},
transclude: {
tplItem: '?tplItem'
}
});

View File

@ -1,20 +1,14 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethod('getLeaves', {
description: 'Returns the first shipped and landed possible for params',
description: 'Returns the nodes for a department',
accepts: [{
arg: 'parentFk',
type: 'Number',
default: 1,
required: false,
},
{
arg: 'filter',
type: 'Object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}
description: 'Get the children of the specified father',
}, {
arg: 'search',
type: 'String',
description: 'Filter nodes whose name starts with',
}],
returns: {
type: ['object'],
@ -26,61 +20,30 @@ module.exports = Self => {
}
});
Self.getLeaves = async(parentFk = 1, filter) => {
let conn = Self.dataSource.connector;
let stmt = new ParameterizedSQL(
`SELECT
child.id,
child.name,
child.lft,
child.rgt,
child.depth,
child.sons
FROM department parent
JOIN department child ON child.lft > parent.lft
AND child.rgt < parent.rgt
AND child.depth = parent.depth + 1
WHERE parent.id = ?`, [parentFk]);
Self.getLeaves = async(parentFk = null, search) => {
let [res] = await Self.rawSql(
`CALL department_getLeaves(?, ?)`,
[parentFk, search]
);
// Get nodes from depth greather than Origin
stmt.merge(conn.makeSuffix(filter));
const nodes = await Self.rawStmt(stmt);
if (nodes.length == 0)
return nodes;
// Get parent nodes
const minorDepth = nodes.reduce((a, b) => {
return b < a ? b : a;
}).depth;
const parentNodes = nodes.filter(element => {
return element.depth === minorDepth;
});
const leaves = Object.assign([], parentNodes);
nestLeaves(leaves);
function nestLeaves(elements) {
elements.forEach(element => {
let childs = Object.assign([], getLeaves(element));
if (childs.length > 0) {
element.childs = childs;
nestLeaves(element.childs);
}
});
let map = new Map();
for (let node of res) {
if (!map.has(node.parentFk))
map.set(node.parentFk, []);
map.get(node.parentFk).push(node);
}
function getLeaves(parent) {
let elements = nodes.filter(element => {
return element.lft > parent.lft && element.rgt < parent.rgt
&& element.depth === parent.depth + 1;
});
return elements;
function setLeaves(nodes) {
if (!nodes) return;
for (let node of nodes) {
node.childs = map.get(node.id);
setLeaves(node.childs);
}
}
return leaves;
let leaves = map.get(parentFk);
setLeaves(leaves);
return leaves || [];
};
};

View File

@ -1,25 +1,26 @@
<vn-crud-model
vn-id="model"
url="/worker/api/departments/getLeaves"
params="::$ctrl.params"
auto-load="false">
</vn-crud-model>
<form name="form">
<div margin-medium>
<div class="content-block" compact>
<form name="form" compact>
<vn-card margin-medium-v pad-medium>
<vn-treeview vn-id="treeview" model="model"
on-selection="$ctrl.onSelection(item, value)"
on-create="$ctrl.onCreate(parent)"
on-drop="$ctrl.onDrop(item, dragged, dropped)"
icons="$ctrl.icons"
draggable="true" droppable="true"
acl-role="hr" editable="true">
icons="$ctrl.icons">
<tpl-item>
test
</tpl-item>
</vn-treeview>
</vn-card>
</div>
</form>
<vn-button-bar>
<vn-button ui-sref="worker.index" label="Back"></vn-button>
</vn-button-bar>
</form>
</div>
<vn-confirm
vn-id="deleteNode"
on-response="$ctrl.onRemoveResponse(response)"

View File

@ -6,7 +6,6 @@ class Controller {
this.$http = $http;
this.vnApp = vnApp;
this.$translate = $translate;
this.params = {parentFk: 1};
this.icons = [{icon: 'delete', tooltip: 'Delete', callback: this.onDelete}];
this.newNode = {
name: ''