window.handleClickBatchFormAction = function(action, $row, rowIndex) { if(action == 'delete') { const $nextRow = this.$tbody.find('tr[data-index="' + rowIndex + '"]'); if($nextRow.length == 0) return; if($nextRow.find('td[data-name=type]').hasClass('hidden')) return; const $prevRow = $nextRow.prev(); if($prevRow.length == 1 && $prevRow.attr('data-level') != $nextRow.attr('data-level') - 1) return; let $typePicker = $nextRow.find('.picker-box[data-name=type]').zui('picker'); let typeItems = []; for(i in typeList) typeItems.push({'text': typeList[i], 'value': i}); $typePicker.render({items: typeItems}); } if(action !== 'addSub' && action !== 'addSibling') return; $this = this; if(action == 'addSub' && $row.find('td[data-name="id"] input[name^=id]').val() > 0) { const $syncData = $row.find('td[data-name="syncData"] input[name^=syncData]'); if($syncData.val() == '0') { const executionID = $row.find('td[data-name="id"] input[name^=id]').val(); $.get($.createLink('project', 'ajaxCheckHasStageData', `executionID=${executionID}`), function(hasData) { if(!hasData) return window.clickToAddRow($this, action, $row, rowIndex); zui.Modal.confirm(confirmCreateTip).then((res) => { if(!res) return; $syncData.val('1'); window.clickToAddRow($this, action, $row, rowIndex); }); }); } else { window.clickToAddRow($this, action, $row, rowIndex); } } else { window.clickToAddRow($this, action, $row, rowIndex); } }; window.clickToAddRow = function($this, action, $row, rowIndex) { if(!$this.nestedLevelMap) $this.nestedLevelMap = {}; const level = $this.nestedLevelMap[$row.attr('data-gid')] || 0; const nextGid = $this._idSeed++; $this.nestedLevelMap[nextGid] = action === 'addSub' ? level + 1 : level; $row.find('input[data-name="estimate"]').prop('readonly', true); // 如果有子任务,不允许修改预计工时 const nextLevel = level + 1; while(true) { $nextRow = $row.next(); if($nextRow.length == 0 || $nextRow.attr('data-level') < nextLevel) { rowIndex = $nextRow.length == 0 ? $row.index() : $nextRow.index() - 1; break; } $row = $nextRow; } $this.addRow(rowIndex, nextGid); }; window.handleRenderRow = function($row, index, data) { if(!this.nestedLevelMap) this.nestedLevelMap = {}; /* 上一行: */ const $prevRow = $row.prev(); /* 添加序号。 */ const $nameTd = $row.find('td[data-name="name"]'); if($nameTd.find('.input-group').length == 0) { $nameTd.find('input.form-control').wrap('
'); $nameTd.find('.input-group').prepend('
'); } /* 从行中查找层级文本展示元素: */ const nestedTextSelector = 'td[data-name="name"] .input-group-addon'; /* 获取当前行的层级,下面可能会根据上一行层级修改当前行层级: */ let level = this.nestedLevelMap[$row.attr('data-gid')] || 0; if(typeof data != 'undefined' && typeof data.grade != 'undefined') level = data.grade - planGrade; /* 当前行层级信息文本: */ let text = '1'; /* 处理有上一行的情况: */ if($prevRow.length) { /* 根据上一行层级,重新计算当前行层级: */ const prevLevel = +$prevRow.attr('data-level') || 0; if(prevLevel < level) level = prevLevel + 1; /* 根据上一行的层级文本,生成当前行的层级文本: */ const prevText = $prevRow.find(nestedTextSelector).text(); const parts = prevText.split('.'); if(prevLevel === level) parts[level] = +parts[level] + 1; else if(prevLevel > level) { parts.length = level + 1; parts[level] = +parts[level] + 1; } else parts[level] = 1; text = parts.join('.'); } else { /* 如果没有上一行,当前行层级为 0: */ level = 0; } /* 存储当前行层级信息: */ this.nestedLevelMap[$row.attr('data-gid')] = level; $row.attr('data-level', level); $row.find(nestedTextSelector).attr('title', text).text(text).append(``); // 创建隐藏表单域用于向服务器提交当前行层级信息。 /* 追加 parent 属性,以记录父级index。 */ let $prevLevelRow = $prevRow; $row.attr('data-parent', '-1'); if($prevRow.length == 1) { if($prevLevelRow.attr('data-level') != level - 1) $prevLevelRow = $row.prevAll('tr[data-level="' + (level - 1) + '"]').first(); if($prevLevelRow.length == 1) $row.attr('data-parent', $prevLevelRow.attr('data-gid')); } if($row.find('input[data-name="milestone"]:checked').length == 0) $row.find('input[data-name="milestone"]').eq(1).prop('checked', true); //里程碑默认选择"否"。 if($prevLevelRow.length && $prevLevelRow.find('input[data-name="syncData"]').val() == '1') $row.find('input[data-name="syncData"]').val(1); /* 处理已有数据字段状态。隐藏的删除按钮,禁用管理方法字段。 */ if(typeof data != 'undefined' && typeof data.id != 'undefined') { $row.find('[data-name="ACTIONS"]').find('[data-type="delete"]').addClass('hidden'); $row.find('[data-name="type"]').find('.picker-box').on('inited', function(e, info){ info[0].render({disabled: true}); }); } /* 如果管理方法不是"阶段",禁用拆分子级按钮,禁用工作量占比字段。 */ const $currentType = $row.find('[data-name="type"] input[name^=type]'); if((data != undefined && data.type != undefined && data.type != 'stage') || ($currentType.length && $currentType.val() != 'stage')) { $row.find('input[data-name="percent"]').prop('disabled', true); $row.find('[data-name="ACTIONS"]').find('[data-type="addSub"]').prop('disabled', true).attr('title', addSubTip); } $row.find('[data-name="type"]').find('.picker-box').on('inited', function(e, info) { let $typePicker = info[0]; let dataExist = (typeof data != 'undefined' && typeof data.type != 'undefined'); let options = {}; options.disabled = (planID == 0 && level == 0) ? true : false; if(dataExist) options.disabled = true; let prevType = ''; if(level > 0) { let $firstChild = $prevLevelRow.next(); if($firstChild.length > 0) { let $firstType = $firstChild.find('[data-name="type"]').find('[name^=type]'); if($firstType.length > 0) prevType = $firstType.val(); } if(prevType && $firstChild.attr('data-index') != $row.attr('data-index')) { options.items = []; for(i in $typePicker.options.items) { let item = $typePicker.options.items[i]; if(item.value == '') continue; if(prevType == 'stage' && item.value == 'stage') options.items.push(item); if(prevType != 'stage' && item.value != 'stage') options.items.push(item); } } } else if(level == 0 && planID && $row.attr('data-index') > 0) { options.items = []; for(i in $typePicker.options.items) { let item = $typePicker.options.items[i]; if(item.value == '') continue; if(initType == 'stage' && item.value == 'stage') options.items.push(item); if(initType != 'stage' && item.value != 'stage') options.items.push(item); } } $typePicker.render(options); if(!dataExist) { if(level > 0 && prevType != '' && prevType != 'stage') $typePicker.$.setValue('sprint'); if(level == 0 && planID) $typePicker.$.setValue(initType); } }); /* Render type picker when move. */ let $typePicker = $row.find('[data-name="type"]').find('.picker-box').zui('picker'); if($typePicker != undefined && level > 0) { let $prevRow = $row.prev(); let typeItems = []; if($prevRow.length == 0 || $prevRow.attr('data-level') < level) { for(i in typeList) typeItems.push({'text': typeList[i], 'value': i}); } else { let type = $row.find('[data-name="type"]').find('[name^=type]').val(); for(i in typeList) { if(type == 'stage' && i == 'stage') typeItems.push({'text': typeList[i], 'value': i}); if(type != 'stage' && i != 'stage') typeItems.push({'text': typeList[i], 'value': i}); } } $typePicker.render({items: typeItems}); } /* 已关闭且有交付物的阶段无法变更阶段类型。 */ $row.find('[data-name="attribute"]').find('.picker-box').on('inited', function(e, info) { if(typeof data != 'undefined' && typeof data.hasDeliverable != 'undefined') info[0].render({disabled: true}); }); if(project.model == 'ipd') { if(planID == 0 && level == 0) { $row.find('[data-name="ACTIONS"]').find('[data-type="sort"]').addClass('disabled').attr('title', sortableTip); $row.find('[data-name="ACTIONS"]').find('[data-type="addSibling"]').prop('disabled', true).attr('title', addSiblingTip); $row.find('[data-name="ACTIONS"]').find('[data-type="delete"]').addClass('hidden'); } $row.find('[data-name="attribute"]').find('.picker-box').on('inited', function(e, info){ info[0].render({disabled: true}); }); const $point = $row.find('[data-name="point"],[data-name="defaultPoint"]'); $point.find('.picker-box').on('inited', function(e, info) { let items = [{text: '', value: ''}]; let disabled = level > 0 ? true : false; if(typeof data != 'undefined' && typeof data.attribute != 'undefined' && !disabled) { const stageID = data.stageID; for(let point in ipdStagePoint[stageID]) { let disabled = false let text = ipdStagePoint[stageID][point]; let hint = text; /* 如果已经评审过,则置灰不能选。*/ if(reviewedPoints[point] !== undefined && reviewedPoints[point].disabled) { disabled = true; hint = reviewedPointTip; } items.push({text: text, value: point, disabled: disabled, hint: hint}); } } info[0].render({items: items, disabled: disabled}); }); const $enabled = $row.find('td[data-name=enabled]'); if($enabled.length > 0 && !$enabled.hasClass('hidden')) { const $checkbox = $enabled.find('input[type=checkbox]'); if(data != undefined && data.status != undefined && data.status != 'wait') $checkbox.attr('disabled', 'disabled').attr('title', cropStageTip); if(level > 0) $checkbox.attr('disabled', 'disabled').attr('title', childEnabledTip); if($enabled.find('input.hidden').length == 0) $enabled.append("") if($enabled.find('input.hidden').length > 0) $enabled.find('input.hidden').attr('name', $checkbox.attr('name')); let $rootRow = $row; if($rootRow.attr('data-level') != 0) $rootRow = $row.prevAll('tr[data-level="0"]').first(); if($rootRow.length == 1 && !$rootRow.find('td[data-name=enabled] input[type=checkbox]').prop('checked')) { $row.addClass('disabled'); $checkbox.prop('checked', false); $enabled.find('input.hidden').val('off'); } } window.waitDom("div.picker-multi-selection", function() { let selection = $row.find('div.picker-multi-selection'); selection.each(function(index, element) { /* 获取评审点ID。*/ let pointID = $(element).find('div.picker-deselect-btn').data('value'); /* 如果该评审点以提交评审,则不能取消选择。*/ if(reviewedPoints[pointID] !== undefined && reviewedPoints[pointID].disabled) { $(element).attr('title', reviewedPointTip); // 修改title $(element).find(".picker-deselect-btn").remove(); // 禁用点击事件 } }); }); } if(level > 0 || planGrade > 1) { let preAttribute = $prevLevelRow.length == 0 ? '' : $prevLevelRow.find('[name^=attribute]').val(); $row.find('[data-name="attribute"]').find('.picker-box').on('inited', function(e, info) { let $attributePicker = info[0]; let disabled = false; if(preAttribute && preAttribute != 'mix') disabled = true; if(data != undefined && data.attribute != undefined && data.attribute != 'mix') disabled = true; if(disabled) { $attributePicker.render({disabled: disabled}); if(preAttribute) $attributePicker.$.setValue(preAttribute); } }); } }; window.onChangeExecutionType = function(event) { loadPage($.createLink('programplan', 'create', `projectID=${projectID}&productID=${productID}&planID=${planID}&type=` + $(event.target).val() + `&from=&syncData=${syncData}`)); }; window.onChangeParallel = function(event) { const parallel = $(event.target).val(); $('input[name^="parallel"]').val(parallel); } window.changeEnabled = function(obj) { const $target = $(obj); const $row = $target.closest('tr'); $row.toggleClass('disabled', !$target.prop('checked')) let $checkbox = $row.find('[data-name=enabled]').find('input[type=checkbox]'); if($row.find('[data-name=enabled]').find('input.hidden') == 0) $row.find('[data-name=enabled]').append(""); $row.find('[data-name=enabled]').find('input.hidden').val($target.prop('checked') ? 'on' : 'off'); let $nextRow = $row.next(); while(true) { if($nextRow.length == 0) break; if($nextRow.attr('data-level') == 0) break; $nextRow.toggleClass('disabled', !$target.prop('checked')) let $nextCheckbox = $nextRow.find('[data-name=enabled]').find('input[type=checkbox]'); if($nextRow.find('[data-name=enabled]').find('input.hidden') == 0) $nextRow.find('[data-name=enabled]').append(""); $nextCheckbox.prop('checked', $target.prop('checked')); $nextRow.find('[data-name=enabled]').find('input.hidden').val($target.prop('checked') ? 'on' : 'off'); $nextRow = $nextRow.next(); } }; window.changeAttribute = function(obj) { const attribute = obj.value; const $target = $(obj); let $row = $target.closest('tr'); let level = $row.attr('data-level'); let $nextRow = $row.next(); while(true) { if($nextRow.length == 0) break; if($nextRow.attr('data-level') <= level) break; let $picker = $nextRow.find('.picker-box[data-name="attribute"]').zui('picker'); $picker.render({disabled: attribute == 'mix' ? false : true}); $picker.$.setValue(attribute); $nextRow = $nextRow.next(); } }; window.changeType = function(obj) { const type = obj.value; const $target = $(obj); let $row = $target.closest('tr'); $row.find('input[data-name="percent"]').prop('disabled', type != 'stage'); $row.find('[data-name="ACTIONS"]').find('[data-type="addSub"]').prop('disabled', type != 'stage').attr('title', type != 'stage' ? addSubTip : ''); let $nextRow = $row.next(); let level = $row.attr('data-level'); let $typePicker = $row.find('.picker-box[data-name=type]').zui('picker'); /* 检查子阶段是否又被拆分,如果有拆分,则不允许修改为非阶段的管理方法。 */ if(type != 'stage') { while(true) { if($nextRow.length == 0) break; if($nextRow.attr('data-level') < level) break; if($nextRow.attr('data-level') > level) { zui.Modal.alert(errorLang.notStage); $typePicker.$.setValue('stage'); return; } $nextRow = $nextRow.next(); } } $nextRow = $row.next(); if($nextRow.length == 0) return; if($nextRow.attr('data-level') != level) return; //只修改同级的管理方法。 $nextRow.find('input[data-name="percent"]').prop('disabled', type != 'stage'); $nextRow.find('[data-name="ACTIONS"]').find('[data-type="addSub"]').prop('disabled', type != 'stage').attr('title', type != 'stage' ? addSubTip : ''); let $nextTypePicker = $nextRow.find('.picker-box[data-name=type]').zui('picker'); if($nextTypePicker == undefined) return; let nextTypeItems = []; for(i in typeList) { if(i == '') continue; if(type == 'stage' && i == 'stage') nextTypeItems.push({'text': typeList[i], 'value': i}); if(type != 'stage' && i != 'stage') nextTypeItems.push({'text': typeList[i], 'value': i}); } $nextTypePicker.render({items: nextTypeItems}); $nextTypePicker.$.setValue(type); }; window.onMove = function(event, originEvent) { const fromLevel = $(event.dragged).attr('data-level'); const toLevel = $(event.related).attr('data-level'); const fromParent = $(event.dragged).attr('data-parent'); const toParent = $(event.related).attr('data-parent'); if(fromLevel != toLevel) return false; if(fromParent != toParent) return false; return true; } window.onSort = function(e) { const gid = $(e.item).attr('data-gid'); const $duplicates = $(`tr[data-gid='${gid}']`); if($duplicates.length > 1) $duplicates.slice(1).remove(); const id = $(e.item).attr('data-parent'); window.moveChildren(id); const $batchForm = $('[data-zui-batchform]').zui('batchForm'); $batchForm._rows = $batchForm._sortable.toArray().map(Number); $batchForm.render(); } window.moveChildren = function(id, processedIds = new Set()) { if(processedIds.has(id)) return; processedIds.add(id); const $parent = $(`tr[data-gid='${id}']`); const $children = $(`tr[data-parent='${id}']`).not('.sortable-empty-shadow'); if($children.length == 0) return; const $reversedChildren = $($children.get().reverse()); $reversedChildren.each(function(index, element) { $parent.after(element); moveChildren($(element).attr('data-gid'), processedIds); }); }