This commit is contained in:
parent
56bc860fa0
commit
3b0d003322
|
@ -1,5 +1,5 @@
|
|||
<md-checkbox
|
||||
aria-label="Checkbox 1"
|
||||
aria-label="{{::$ctrl.label}}"
|
||||
md-indeterminate="$ctrl.isIntermediate"
|
||||
ng-disabled="$ctrl.disabled"
|
||||
ng-checked="$ctrl.isChecked"
|
||||
|
|
|
@ -2,15 +2,16 @@
|
|||
|
||||
vn-check {
|
||||
position: relative;
|
||||
& > .mdl-checkbox {
|
||||
width: initial;
|
||||
}
|
||||
|
||||
& > i {
|
||||
padding-left: 5px;
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
color: $color-font-secondary;
|
||||
font-size: 20px !important
|
||||
font-size: 20px !important;
|
||||
cursor: help
|
||||
}
|
||||
|
||||
md-checkbox.md-checked .md-icon {
|
||||
background-color: $color-main;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
<ul ng-if="$ctrl.items">
|
||||
<li ng-repeat="item in $ctrl.items" ng-class="{'selected' : item.selected && !item.excluded, 'included': item.included}">
|
||||
<li ng-repeat="item in $ctrl.items"
|
||||
ng-class="{
|
||||
'expanded': item.active,
|
||||
'collapsed': !item.active,
|
||||
'included': item.isIncluded == 1,
|
||||
'excluded': item.isIncluded == 0
|
||||
}">
|
||||
<vn-horizontal>
|
||||
<vn-auto class="actions">
|
||||
<vn-icon icon="keyboard_arrow_up" ng-if="item.childs.length"
|
||||
ng-click="$ctrl.toggle(item, $event)">
|
||||
</vn-icon>
|
||||
<vn-icon icon="keyboard_arrow_down" ng-if="item.sons > 0 && !item.childs"
|
||||
ng-click="$ctrl.toggle(item, $event)">
|
||||
<vn-icon icon="keyboard_arrow_down"
|
||||
ng-show="item.sons > 0"
|
||||
ng-click="$ctrl.toggle(item, $event)" >
|
||||
</vn-icon>
|
||||
</vn-auto>
|
||||
<vn-one class="description">
|
||||
<vn-horizontal>
|
||||
<vn-check vn-auto field="item.selected && !item.excluded"
|
||||
on-change="$ctrl.select(item)">
|
||||
<div class="description">
|
||||
<vn-check vn-auto field="item.isIncluded"
|
||||
on-change="$ctrl.select(item, value)" triple-state="true">
|
||||
</vn-check>
|
||||
<vn-one ng-dblclick="$ctrl.toggle(item)" class="text unselectable">
|
||||
{{::item.name}}
|
||||
</vn-one>
|
||||
</vn-horizontal>
|
||||
</vn-one>
|
||||
</div>
|
||||
</vn-horizontal>
|
||||
<vn-treeview-child items="item.childs"></vn-treeview-child>
|
||||
</li>
|
||||
|
|
|
@ -10,8 +10,8 @@ class Controller extends Component {
|
|||
this.treeview.onToggle(item);
|
||||
}
|
||||
|
||||
select(item) {
|
||||
this.treeview.onSelection(item);
|
||||
select(item, value) {
|
||||
this.treeview.onSelection(item, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,72 +20,10 @@ export default class Treeview extends Component {
|
|||
refresh() {
|
||||
this.model.refresh().then(() => {
|
||||
this.data = this.model.data;
|
||||
console.log(this.data);
|
||||
this.repaintAll();
|
||||
});
|
||||
}
|
||||
|
||||
repaintAll() {
|
||||
let oldData = this.data;
|
||||
oldData.forEach(node => {
|
||||
this.repaintAsc(node);
|
||||
this.repaintDesc(node);
|
||||
});
|
||||
}
|
||||
|
||||
repaintNode(node) {
|
||||
this.repaintAsc(node);
|
||||
this.repaintDesc(node);
|
||||
}
|
||||
|
||||
repaintAsc(node) {
|
||||
if (!node.parent) return;
|
||||
|
||||
const parent = node.parent;
|
||||
if ((node.selected || node.included) && !parent.selected) {
|
||||
parent.included = true;
|
||||
parent.hasCheckedChilds = true;
|
||||
} else if (!this.hasCheckedChilds(parent) && !this.hasCheckedParents(node))
|
||||
parent.included = false;
|
||||
|
||||
// FIXME - Propagate hasCheckedCHilds
|
||||
if (!node.selected && this.hasCheckedParents(node)) {
|
||||
node.included = true;
|
||||
parent.hasCheckedChilds = false;
|
||||
}
|
||||
|
||||
if (!this.hasCheckedChilds(node))
|
||||
node.hasCheckedChilds = false;
|
||||
|
||||
this.repaintAsc(parent);
|
||||
}
|
||||
|
||||
repaintDesc(node) {
|
||||
/* if (node.hasCheckedChilds)
|
||||
node.included = false; */
|
||||
|
||||
if (!node.selected && this.hasCheckedChilds(node)) {
|
||||
node.hasCheckedChilds = true;
|
||||
node.included = true;
|
||||
} else if (!node.selected && node.childs && !this.hasCheckedChilds(node))
|
||||
node.hasCheckedChilds = false;
|
||||
|
||||
|
||||
const childs = node.childs || [];
|
||||
for (let i = 0; i < childs.length; i++) {
|
||||
childs[i].included = false;
|
||||
|
||||
if (((node.selected || node.included && this.hasCheckedParents(childs[i])) && !childs[i].selected) && !node.excluded)
|
||||
childs[i].included = true;
|
||||
|
||||
this.repaintDesc(childs[i]);
|
||||
}
|
||||
|
||||
if (!node.selected && node.hasCheckedChilds)
|
||||
node.included = true;
|
||||
}
|
||||
|
||||
hasCheckedChilds(node) {
|
||||
/* hasCheckedChilds(node) {
|
||||
if (!node.childs) return false;
|
||||
|
||||
const childs = node.childs;
|
||||
|
@ -105,44 +43,34 @@ export default class Treeview extends Component {
|
|||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
} */
|
||||
|
||||
onSelection(item) {
|
||||
let canBeExcluded = this.hasCheckedChilds(item) || this.hasCheckedParents(item);
|
||||
|
||||
if (!item.selected && item.included && canBeExcluded)
|
||||
item.excluded = true;
|
||||
else
|
||||
item.excluded = false;
|
||||
|
||||
item.selected = !item.selected;
|
||||
|
||||
if (item.selected && item.included)
|
||||
item.included = false;
|
||||
|
||||
if (this.hasCheckedChilds(item))
|
||||
item.hasCheckedChilds = true;
|
||||
else if (this.childs)
|
||||
item.hasCheckedChilds = false;
|
||||
|
||||
this.emit('selection', {item});
|
||||
onSelection(item, value) {
|
||||
this.emit('selection', {item, value});
|
||||
}
|
||||
|
||||
onToggle(item) {
|
||||
if (item.childs && item.childs.length == 0)
|
||||
return;
|
||||
|
||||
if (item.childs)
|
||||
if (item.active)
|
||||
item.childs = undefined;
|
||||
else {
|
||||
this.model.applyFilter({}, {parentFk: item.id}).then(() => {
|
||||
item.childs = this.model.data;
|
||||
item.childs.forEach(child => {
|
||||
child.parent = item;
|
||||
const newData = this.model.data;
|
||||
|
||||
if (item.childs) {
|
||||
let childs = item.childs;
|
||||
childs.forEach(child => {
|
||||
let index = newData.findIndex(newChild => {
|
||||
return newChild.id == child.id;
|
||||
});
|
||||
this.repaintNode(item);
|
||||
newData[index] = child;
|
||||
});
|
||||
}
|
||||
|
||||
item.childs = newData.sort((a, b) => b.isIncluded - a.isIncluded);
|
||||
});
|
||||
}
|
||||
|
||||
item.active = !item.active;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,19 +2,21 @@
|
|||
|
||||
vn-treeview {
|
||||
ul {
|
||||
margin: 0;
|
||||
line-height: 24px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
|
||||
.actions {
|
||||
padding: 0.5em;
|
||||
min-width: 24px;
|
||||
}
|
||||
|
||||
.description {
|
||||
padding: 0.5em
|
||||
padding-left: 5px
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,27 +24,41 @@ vn-treeview {
|
|||
padding: 0 1.8em;
|
||||
}
|
||||
|
||||
li > vn-horizontal {
|
||||
padding: 5px
|
||||
}
|
||||
|
||||
li > vn-horizontal:hover {
|
||||
background-color: $color-hover-cd
|
||||
}
|
||||
|
||||
li.selected > vn-horizontal > .description .text,
|
||||
li.included > vn-horizontal > .description .text {
|
||||
font-weight: bold;
|
||||
color: $color-main;
|
||||
li.expanded .actions > vn-icon[icon="keyboard_arrow_down"] {
|
||||
transition: all 0.2s;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
li.collapsed .actions > vn-icon[icon="keyboard_arrow_down"] {
|
||||
transition: all 0.2s;
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
li.included {
|
||||
& > vn-horizontal > .description > vn-horizontal > vn-check {
|
||||
.mdl-checkbox .mdl-checkbox__box-outline, {
|
||||
border: 2px solid $color-main-medium;
|
||||
& > vn-horizontal > .description {
|
||||
color: $color-notice;
|
||||
font-weight: bold;
|
||||
|
||||
& > vn-check .md-icon {
|
||||
background-color: $color-notice
|
||||
}
|
||||
fieldset[disabled] .mdl-checkbox .mdl-checkbox__box-outline, .mdl-checkbox.is-disabled .mdl-checkbox__box-outline {
|
||||
border: 2px solid rgba(0,0,0,.26);
|
||||
}
|
||||
.mdl-checkbox .mdl-checkbox__tick-outline {
|
||||
background: $color-main-medium;
|
||||
|
||||
}
|
||||
|
||||
li.excluded {
|
||||
& > vn-horizontal > .description {
|
||||
color: $color-alert;
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,6 @@ module.exports = Self => {
|
|||
description: 'Returns the first shipped and landed possible for params',
|
||||
accessType: '',
|
||||
accepts: [{
|
||||
arg: 'filter',
|
||||
type: 'Object',
|
||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
http: {source: 'query'}
|
||||
},
|
||||
{
|
||||
arg: 'zoneFk',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
|
@ -21,6 +15,12 @@ module.exports = Self => {
|
|||
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'}
|
||||
}],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
|
@ -32,64 +32,106 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.getLeaves = async(filter, zoneFk, parentFk = 1) => {
|
||||
Self.getLeaves = async(zoneFk, parentFk = 1, filter) => {
|
||||
let conn = Self.dataSource.connector;
|
||||
let stmts = [];
|
||||
|
||||
let geo = await Self.findById(parentFk);
|
||||
|
||||
stmts.push(`DROP TEMPORARY TABLE IF EXISTS tmp.checkedChilds`);
|
||||
stmts.push(new ParameterizedSQL(
|
||||
`SELECT lft, rgt, depth + 1 INTO @lft, @rgt, @depth
|
||||
FROM zoneGeo WHERE id = ?`, [parentFk]));
|
||||
`CREATE TEMPORARY TABLE tmp.checkedChilds (
|
||||
id INT,
|
||||
name VARCHAR(100),
|
||||
lft INT,
|
||||
rgt INT,
|
||||
depth BIGINT(22),
|
||||
sons DECIMAL(10, 0),
|
||||
isIncluded TINYINT
|
||||
) ENGINE = MEMORY`));
|
||||
|
||||
stmts.push(`DROP TEMPORARY TABLE IF EXISTS tChilds`);
|
||||
|
||||
let stmt = new ParameterizedSQL(
|
||||
`CREATE TEMPORARY TABLE tChilds
|
||||
ENGINE = MEMORY
|
||||
SELECT id, lft, rgt
|
||||
FROM zoneGeo pt`);
|
||||
stmt.merge(conn.makeSuffix(filter));
|
||||
|
||||
if (!filter.where) {
|
||||
stmt.merge(`WHERE pt.lft > @lft AND pt.rgt < @rgt
|
||||
AND pt.depth = @depth`);
|
||||
if (parentFk === 1) {
|
||||
stmts.push(new ParameterizedSQL(
|
||||
`INSERT INTO tmp.checkedChilds
|
||||
SELECT
|
||||
zg.id,
|
||||
zg.name,
|
||||
zg.lft,
|
||||
zg.rgt,
|
||||
zg.depth,
|
||||
zg.sons,
|
||||
zi.isIncluded
|
||||
FROM zoneGeo zg
|
||||
JOIN zoneIncluded zi ON zi.geoFk = zg.id
|
||||
AND zoneFk = ?`, [zoneFk]));
|
||||
}
|
||||
|
||||
stmts.push(stmt);
|
||||
let stmt = new ParameterizedSQL(
|
||||
`SELECT * FROM (
|
||||
SELECT
|
||||
zg.id,
|
||||
zg.name,
|
||||
zg.lft,
|
||||
zg.rgt,
|
||||
zg.depth,
|
||||
zg.sons,
|
||||
IF(ch.id = zg.id, isIncluded, null) isIncluded
|
||||
FROM zoneGeo zg
|
||||
JOIN tmp.checkedChilds ch ON zg.lft <= ch.lft AND zg.rgt >= ch.rgt
|
||||
UNION ALL
|
||||
SELECT
|
||||
child.id,
|
||||
child.name,
|
||||
child.lft,
|
||||
child.rgt,
|
||||
child.depth,
|
||||
child.sons,
|
||||
zi.isIncluded
|
||||
FROM zoneGeo parent
|
||||
JOIN zoneGeo child ON child.lft > parent.lft
|
||||
AND child.rgt < parent.rgt
|
||||
AND child.depth = parent.depth + 1
|
||||
LEFT JOIN zoneIncluded zi ON zi.geoFk = child.id
|
||||
AND zoneFk = ?
|
||||
WHERE parent.id = ?) AS nst`, [zoneFk, parentFk]);
|
||||
|
||||
stmts.push(`DROP TEMPORARY TABLE IF EXISTS tZones`);
|
||||
stmts.push(new ParameterizedSQL(
|
||||
`CREATE TEMPORARY TABLE tZones
|
||||
(INDEX (id))
|
||||
ENGINE = MEMORY
|
||||
SELECT t.id
|
||||
FROM tChilds t
|
||||
JOIN zoneGeo zt
|
||||
ON zt.lft > t.lft AND zt.rgt < t.rgt
|
||||
JOIN zoneIncluded zi
|
||||
ON zi.geoFk = zt.id AND zi.zoneFk = ?
|
||||
GROUP BY t.id`, [zoneFk]));
|
||||
// Get nodes from depth greather than Origin
|
||||
stmt.merge(conn.makeSuffix(filter));
|
||||
stmt.merge('GROUP BY nst.id');
|
||||
|
||||
const resultIndex = stmts.push(new ParameterizedSQL(
|
||||
`SELECT
|
||||
pt.id,
|
||||
pt.name,
|
||||
pt.lft,
|
||||
pt.rgt,
|
||||
pt.depth,
|
||||
pt.sons,
|
||||
ti.id IS NOT NULL hasCheckedChilds,
|
||||
zi.geoFk IS NOT NULL AS selected,
|
||||
zi.isIncluded AS excluded
|
||||
FROM zoneGeo pt
|
||||
LEFT JOIN vn.zoneIncluded zi
|
||||
ON zi.geoFk = pt.id AND zi.zoneFk = ?
|
||||
JOIN tChilds c ON c.id = pt.id
|
||||
LEFT JOIN tZones ti ON ti.id = pt.id
|
||||
ORDER BY selected DESC, name`, [zoneFk])) - 1;
|
||||
const resultIndex = stmts.push(stmt) - 1;
|
||||
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await Self.rawStmt(sql);
|
||||
const nodes = result[resultIndex];
|
||||
|
||||
return result[resultIndex];
|
||||
// Get parent nodes
|
||||
const parentNodes = nodes.filter(element => {
|
||||
return element.depth === geo.depth + 1;
|
||||
});
|
||||
|
||||
const sortedLeaves = parentNodes.sort((a, b) => b.isIncluded - a.isIncluded);
|
||||
const leaves = Object.assign([], sortedLeaves);
|
||||
|
||||
nestLeaves(leaves);
|
||||
|
||||
function nestLeaves(elements) {
|
||||
elements.forEach(element => {
|
||||
element.childs = Object.assign([], getLeaves(element));
|
||||
|
||||
nestLeaves(element.childs);
|
||||
});
|
||||
}
|
||||
|
||||
function getLeaves(parent) {
|
||||
let elements = nodes.filter(element => {
|
||||
return element.lft > parent.lft && element.rgt < parent.rgt
|
||||
&& element.depth === parent.depth + 1;
|
||||
});
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
return leaves;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,8 +8,8 @@ module.exports = Self => {
|
|||
required: true,
|
||||
},
|
||||
{
|
||||
arg: 'geoFk',
|
||||
type: 'Number',
|
||||
arg: 'item',
|
||||
type: 'Object',
|
||||
required: true,
|
||||
}],
|
||||
returns: {
|
||||
|
@ -22,31 +22,15 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.toggleIsIncluded = async(zoneFk, geoFk) => {
|
||||
const models = Self.app.models;
|
||||
const geo = await models.ZoneGeo.findById(geoFk);
|
||||
const isIncluded = await Self.findOne({
|
||||
where: {zoneFk, geoFk}
|
||||
Self.toggleIsIncluded = async(zoneFk, item) => {
|
||||
if (item.isIncluded === null)
|
||||
return Self.destroyAll({zoneFk, geoFk: item.id});
|
||||
else {
|
||||
return Self.upsert({
|
||||
zoneFk: zoneFk,
|
||||
geoFk: item.id,
|
||||
isIncluded: item.isIncluded
|
||||
});
|
||||
|
||||
const hasCheckedParents = await Self.rawSql(
|
||||
`SELECT id
|
||||
FROM vn.zoneGeo zt
|
||||
JOIN vn.zoneIncluded zi ON zi.geoFk = zt.id
|
||||
WHERE zt.lft < ? AND zt.rgt > ?`, [geo.lft, geo.rgt]
|
||||
);
|
||||
const hasCheckedChilds = await Self.rawSql(
|
||||
`SELECT id
|
||||
FROM vn.zoneGeo zt
|
||||
JOIN vn.zoneIncluded zi ON zi.geoFk = zt.id
|
||||
WHERE zt.lft > ? AND zt.rgt < ?`, [geo.lft, geo.rgt]
|
||||
);
|
||||
|
||||
const isExcluded = hasCheckedParents.length || hasCheckedChilds.length;
|
||||
|
||||
if (isIncluded)
|
||||
return Self.destroyAll({zoneFk, geoFk});
|
||||
else
|
||||
return Self.upsert({zoneFk, geoFk, isIncluded: isExcluded});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ class Controller {
|
|||
res.data.forEach(holiday => {
|
||||
events.push({
|
||||
date: holiday.dated,
|
||||
color: 'blue-circle',
|
||||
color: 'red',
|
||||
title: holiday.description || holiday.name,
|
||||
isRemovable: false
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
vn-id="model"
|
||||
url="/agency/api/ZoneGeos/getLeaves"
|
||||
filter="::$ctrl.filter"
|
||||
params="{zoneFk: $ctrl.$stateParams.id, parentFk: 1}">
|
||||
params="{zoneFk: $ctrl.$stateParams.id}" auto-load="false">
|
||||
</vn-crud-model>
|
||||
<div class="main-with-right-menu">
|
||||
<vn-card compact pad-large>
|
||||
|
@ -13,7 +13,7 @@
|
|||
vn-focus>
|
||||
</vn-searchbar>
|
||||
<vn-treeview vn-id="treeview" model="model"
|
||||
on-selection="$ctrl.onSelection(item)">
|
||||
on-selection="$ctrl.onSelection(item, value)">
|
||||
</vn-treeview>
|
||||
</vn-card>
|
||||
<vn-side-menu side="right">
|
||||
|
|
|
@ -22,11 +22,12 @@ class Controller {
|
|||
}
|
||||
}
|
||||
|
||||
onSelection(item) {
|
||||
onSelection(item, isIncluded) {
|
||||
item.isIncluded = isIncluded;
|
||||
const path = '/agency/api/ZoneIncludeds/toggleIsIncluded';
|
||||
const params = {geoFk: item.id, zoneFk: this.zone.id};
|
||||
const params = {zoneFk: this.zone.id, item};
|
||||
this.$http.post(path, params).then(() => {
|
||||
this.$scope.treeview.repaintNode(item);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue