自定义单位
自定义单元混合组件(附加组件)可调整板、列表和时间轴视图中的自定义卡片功能。
视图中的卡片可灵活配置。 每种尺码(S、M、L等)的卡片布局。 由多个单元(字段)组成。 许多有用的单位已预先定义好。 然而,可以扩展此列表并实现未预装的 images/ 功能。

他们进行高级计算的能力使自定义单元组合功能变得强大。 该系统能够利用多个源字段和属性、常见数学运算以及字符串操作来评估值。 因此,您将获得经过计算的数值、日期、布尔值和文本值。 它们可以显示在卡片视图、列表视图和时间轴视图中。
已创建的自定义单位示例
以下列举了可通过混合应用添加的卡片和列表的自定义单元示例。
- 彩色自定义字段 - 用于为显示的自定义字段中的值定义彩色高亮规则。
- 描述 - 描述文本框的前150个字符。
- 最后一条评论 - 最近评论文本的前150个字符。
- 附件 - 首个附加(上传)图像文件的缩略图。
- 所有已分配人员 - 显示所有已分配用户的姓名首字母缩写。
- 所有者全名 - 所有者用户的姓名(包含姓氏和名字)。
- 关系计数器 - 任何实体的入站和出站关系中,已打开项目与所有项目的计数。 类型 Link 的关系与所有其他类型均单独计数。
- 按角色分配任务——按角色分别显示已分配用户;当具有多重角色的用户对实体进行估算并处理时,此功能尤为实用
- 角色工作量 - 按角色分别显示角色工作量(待完成工作量、已完成工作量)、已耗时间和剩余时间;当具有多重角色的用户估算实体并处理它们时,此功能尤为实用。
- 父实体计划日期、发布迭代和团队迭代日期——显示父级功能/用户故事/发布/迭代(冲刺)/团队迭代(对应用户故事、任务、缺陷或功能)的起止日期。
- 项目当前版本与迭代 - 显示项目当前版本与迭代的名称及可点击ID
- 请求者 - 请求者的名称显示在代表请求的卡片上。
- 请求方所属公司——请求方的名片上会显示公司名称。
- TestCasePercentage - 通过与失败的最近运行测试用例百分比,或自定义状态百分比,按测试用例类型分别计算;每个测试用例的类型通过下拉自定义字段指定。 适用于回归测试和验收工作流。
高级混合应用不仅能展示信息,还能在卡片间建立交互,并允许在视图中直接修改卡片:
- 显示关联 - 卡片间的关联关系在看板视图和列表视图中以箭头形式呈现。
- 快速添加评论 - 提交评论。
如何组合自定义单位混搭
让我们创建一个混合应用程序,该应用程序将以自定义单位显示实体名称的长度。
创建具有以下依赖项的混合应用:
tau.mashups
.addDependency('tau/models/board.customize.units/const.entity.types.names')
.addDependency('tau/models/board.customize.units/const.card.sizes')
.addDependency('tau/api/board/v1/customize')
.addMashup(function(types, sizes, customizeApi) {
//insert your custom code here
});在您的自定义代码中,您需要执行以下操作:定义单元对象。 这应该是一个包含以下字段的对象:id、 classId, name、types、sizes、template、 sampleData, model。| FieldName | 用途 | 示例 |
|---|---|---|
| 标识 | 唯一标识符。 | 不含空格。 名称长度 |
| classId | 应用于该单元的CSS类ID。 用于进一步应用自定义样式。 | tau-板单元类型名称长度 |
| 名称 | 人类可读名称。 显示在库中以及列表列标题中。 | 名称长度 |
| 类型 | 本单元可适用的实体类型。 | [ types.EPIC, types.FEATURE, types.STORY, types.TASK, types.BUG] |
| 尺寸 | 本设备可适用的卡片尺寸。 | [sizes.XS, sizes.S, sizes.M, sizes.L, sizes.XL, sizes.LIST] |
| hideIf | 布尔函数,用于确定该单元是否应显示或隐藏。 返回 'true' 以隐藏该单元。 | |
| 模板 | 用于渲染数据的HTML标记。 Targetprocess 使用 jQote2 作为模板引擎。 | '<%= String( this.data.nameLength[0 ] || 0) %>' |
| sampleData | 本单元在自定义卡片向导中使用的默认数据。 格式为 JSON 对象。 | nameLength: 42 |
| 模型 | 该单元的数据是如何从各个实体中计算得出的。 REST API 采用 v2 选择器表示法。 模型可以是字符串,也可以是对象。 |
nameLength:Name.Length |
| csv | 将自定义单位作为一列添加到导出的 CSV 文件中。 也支持多个列。 列属性: id 是唯一标识符。 不含空格。 最好将其设置为与自定义单元 ID 相同。 name 是导出 CSV 文件中的列名。 有时你可能希望将某个实体名称作为整个列名称的一部分使用。 为了正确地完成这项操作,你应该使用特殊的语法。 假设“Release”实体类型的术语已更改为单数形式“Overridden Release”和复数形式“Overridden Releases”。 左侧的语法展示了在混合应用中应如何编写名称,以获得右侧(=> 之后)的导出结果: ' ${Release} 条款' => 已覆盖的发布条款 ' ${release} 条款' => 已覆盖的发布条款 ' ${Release,default} 条款' => 已覆盖的发布条款 “ ${Release,lower} 条款” => 被覆盖的发布条款 “ ${Release,upper} 条款” => 被覆盖的RELEASE条款 ' ${Release,capital} 条款' => 已覆盖的发布条款 ' ${Release,camel} 条款' => 已覆盖的发布条款 modelsByEntityType 这是一个可选的数组,用于存储每个实体类型的专用模型。 当默认模型不适用于特定实体类型时,此方法会很有用。 在本例中,我们使用 ` 'EntityType ` 覆盖默认模型 ` Name.Length `。对于 `Task`,我们设置 ` Name.Length `,因此 `Task` 的导出值始终等于 4。 限制:仅限列表视图。 您的账户应已启用“ csvCustomizedExport ”功能(该功能默认处于启用状态)。 您可以通过访问 /api/featureToggling/v1 端点来检查该功能是否已启用,或者联系支持团队寻求帮助。 |
{{columns: [{id: 'name_length',name: '${Name Length} ',model: 'Name.Length',modelsByEntityType: [ {types: [types.TASK],model: 'EntityType.Name.Length',} ]}, {{id: 'type_name_length',name: '${EntityType Name Length} ',model: ''EntityType. Name.Length '}]} |
| 类别 | 仅适用于列表视图。 新增按自定义单位列排序列表视图的选项。 可能的选项: simple => 这些是实体的属性,例如 Name、Effort、Create Date 等。 一对一 => 用于实体,例如在显示任务的Epic时。 |
简式 |
| sortConfig | 仅适用于列表视图。 与类别字段配合使用。 支持按自定义单位列排序。 | {field: 'Name.Length',inverseOrder: false} |
- 自定义字段 => 用于自定义字段。
- 现在支持所有扩展数据模型( ExD Extended Data Model)实体以及这些原生实体类型:
types.EPIC, types.FEATURE, types.STORY,
types.REQUEST, types.PORTFOLIO_EPIC,
types.BUG, types.TASK, types.TIME.
该单元的外观如下:
var unit = {
id: 'name_length',
name: 'Name Length',
classId: 'tau-board-unit_type_name_length',
types: [types.EPIC, types.FEATURE, types.STORY, types.TASK, types.BUG],
template: [
'<div class="tau-board-unit__value"><%= String(this.data.nameLength || 0) %></div>'
],
sizes: [sizes.L, sizes.XL, sizes.LIST],
sampleData: {
nameLength: 42
},
model: 'nameLength:Name.Length',
csv: {
columns: [{
id: 'name_length',
name: '${Name Length}',
model: 'Name.Length'
}]
},
category: 'simple',
sortConfig: {
field: 'Name.Length',
inverseOrder: false
}
};最后一步是注册该设备:customizeApi.registerCustomUnit(unit);我们快完成了。 现在让我们将代码的所有部分组合在一起:tau.mashups
.addDependency('tau/models/board.customize.units/const.entity.types.names')
.addDependency('tau/models/board.customize.units/const.card.sizes')
.addDependency('tau/api/board/v1/customize')
.addMashup(function(types, sizes, customizeApi) {
var unit = {
id: 'name_length',
name: 'Name Length',
classId: 'tau-board-unit_type_name_length',
types: [types.EPIC, types.FEATURE, types.STORY, types.TASK, types.BUG],
template: [
'<div class="tau-board-unit__value"><%= String(this.data.nameLength || 0) %></div>'
],
sizes: [sizes.L, sizes.XL, sizes.LIST],
sampleData: {
nameLength: 42
},
model: 'nameLength:Name.Length',
csv: {
columns: [{
id: 'name_length',
name: '${Name Length}',
model: 'Name.Length'
}]
},
category: 'simple',
sortConfig: {
field: 'Name.Length',
inverseOrder: false
}
};
customizeApi.registerCustomUnit(unit);
});现在让我们将此混合应用程序安装到您的Targetprocess应用程序中。- 打开设置 > 系统设置 > 混合应用。
- 在混合应用管理器中,点击 “添加新混合应用 ”按钮。
- 给这个混搭作品命名 CustomUnitNameLength
- 将占位符设置为 restui_board。
- 将源代码复制并粘贴到代码区域。
- 点击 “保存混合内容 ”按钮。
- 请在您的网页浏览器中重新加载所有已打开的Targetprocess页面,以便应用更改。
已完成:名为 的混合 CustomUnitNameLength 应用已出现在已安装混合应用列表中。
组合式混搭的使用
现在让我们创建一个新的列表视图,将史诗/功能/用户故事/任务/缺陷/测试计划/请求以卡片形式展示。 在设置视图向导中,打开 “自定义卡片 ”选项卡。 在可用单位中,找到名称长度单位。 将其拖拽至卡片布局。
然后完成视图的设置。 在列表视图中,新增了名为“名称长度 ”的列。 该混合程序会计算每个用户故事名称的长度,并将其显示在此列中。 
实体类型
| 目标进程实体 |
|---|
| ExD 实体(扩展域) |
| 用户案例 |
| 任务 |
| 错误 |
| 特性 |
| Epic |
| 请求 |
| 请求者 |
| 项目 |
| 团队 |
| 计划 |
| 解除 |
| 迭代(冲刺) |
| 团队迭代 |
| 构建 |
| 障碍 |
| 测试用例 |
| 测试计划 |
| 测试计划运行 |
| 关系:入站关系,出站关系 |
| 任何实体类型 |
卡片尺寸
| 卡片尺寸 | 描述 | 在混合应用中使用尺寸 |
|---|---|---|
| XS | 仅基本信息,仅一行。 可能是卡牌名称或ID。 | sizes.XS |
| S | 此处可容纳更多信息:例如两行内容,如卡片名称+已分配人员的头像。 | sizes.S |
| M | 更多信息:三行。 可能是卡牌名称 + 标签 + 头像及花费时间。 | sizes.M |
| L | 此尺寸用于详细信息。 无限行。 | sizes.L |
| XL | 超长弹力修身款。 这是卡片在列表模式下的显示效果。 | sizes.XL |
| 列表 | 在最小空间内显示最大信息量。 允许无限嵌套列表。 | sizes.LIST |
单位的自定义样式
我们可以定义特殊函数 addCSSRule ,以扩展默认的Targetprocess样式表,并在其中为新添加的图片附加自定义CSS类。var style = document.createElement("style");
style.setAttribute('id', 'NameLengthCustomUnit');
style.appendChild(document.createTextNode(""));
document.head.appendChild(style);
var sheet = style.sheet;
var addCSSRule = function(selector, rules, index) {
if (index === undefined) {
index = sheet.cssRules.length;
}
sheet.insertRule(selector + "{" + rules + "}", index);
};在此示例中,我们定义了一个具有唯一ID的新CSS样式, NameLengthCustomUnit 同时还新增了类名tau-board-unit_type_name_length。 我们添加了两条不同的规则来定制字体大小:一条用于看板视图中的卡片,另一条则专用于XL尺寸的卡片。addCSSRule(
'.tau-card-v2__section .tau-board-unit_type_name_length .tau-board-unit__value',
'font-size: 14px;');
addCSSRule(
'.tau-ui-size-xl .tau-card-v2__section .tau-board-unit_type_name_length .tau-board-unit__value',
'font-size: 16px;');
列表中可编辑的自定义单元
默认情况下,自定义单位在列表视图中不可编辑。 您可以在混合应用中使用 tau/api/board/v1/customize '' API 扩展单元行为,使其可编辑。 例如,我们有一个自定义单元,希望使其可编辑:tau.mashups
.addDependency('tau/models/board.customize.units/const.entity.types.names')
.addDependency('tau/models/board.customize.units/const.card.sizes')
.addDependency('tau/api/board/v1/customize')
.addMashup(function(types, sizes, customizeApi) {
const unit = {
id: 'due_date_colored',
classId: 'tau-board-unit_type_due-date-colored',
name: 'Formatted Due Date',
types: [ types.STORY, types.FEATURE ],
hideIf : function (data) {
return !data.due;
},
sizes: [ sizes.XS, sizes.S, sizes.M, sizes.L, sizes.XL, sizes.LIST ],
template: {
markup: [
'<div class="tau-board-unit__value">',
'<%= fn.getDate(this.data.due, this.data.closed) %>',
'</div>'
],
customFunctions: {
getDate: function(due, closed) {
var today = new Date();
var date = new Date(due.match(/\d+/)[0] * 1 + 6*60*60*1000);
var color = !closed && (date - today) / (1000 * 60 * 60 * 60) < 1 ? 'red' : 'black';
return "<span style=\"color:" + color + "\"><b>"
+ $.datepicker.formatDate('dd MMM yyyy', date)
+ "</b></span>";
}
}
},
sampleData: { due: '\/Date(1385360733000-0500)\/', closed: true },
model: 'due:CustomValues.Date("Due Date"),closed:(entityState.isFinal or entityState.Name="Cancelled")'
};
customizeApi.registerCustomUnit(unit);
});
截止日期是用户故事实体的自定义字段。 在列表视图中将显示为:
为了使该单元可编辑,我们需要引入“ tau/api/board/v1/customize ”依赖项,并在注册前使用它来扩展单元的行为:const editableCustomFieldUnit = customizeApi.makeCustomFieldUnitEditable(unit,'Due Date');
customizeApi.registerCustomUnit(editableCustomFieldUnit);就这样,现在我们有了一个可编辑的自定义单元:mashup
的最终版本将如下所示:tau.mashups
.addDependency('tau/models/board.customize.units/const.entity.types.names')
.addDependency('tau/models/board.customize.units/const.card.sizes')
.addDependency('tau/api/board/v1/customize')
.addMashup(function(types, sizes, customizeApi) {
const unit = {
id: 'due_date_colored',
classId: 'tau-board-unit_type_due-date-colored',
name: 'Formatted Due Date',
types: [ types.STORY, types.FEATURE ],
hideIf : function (data) {
return !data.due;
},
sizes: [ sizes.XS, sizes.S, sizes.M, sizes.L, sizes.XL, sizes.LIST ],
template: {
markup: [
'<div class="tau-board-unit__value">',
'<%= fn.getDate(this.data.due, this.data.closed) %>',
'</div>'
],
customFunctions: {
getDate: function(due, closed) {
var today = new Date();
var date = new Date(due.match(/\d+/)[0] * 1 + 6*60*60*1000);
var color = !closed && (date - today) / (1000 * 60 * 60 * 60) < 1 ? 'red' : 'black';
return "<span style=\"color:" + color + "\"><b>"
+ $.datepicker.formatDate('dd MMM yyyy', date)
+ "</b></span>";
}
}
},
sampleData: { due: '\/Date(1385360733000-0500)\/', closed: true },
model: 'due:CustomValues.Date("Due Date"),closed:(entityState.isFinal or entityState.Name="Cancelled")'
};
const editableCustomFieldUnit = customizeApi.makeCustomFieldUnitEditable(unit,'Due Date');
customizeApi.registerCustomUnit(editableCustomFieldUnit);
});
支持可编辑的自定义单位
现已支持自定义字段、 州/省和日期编辑器在 tau/api/board/v1/customize ''中使用:| 单元类型 | 方法 |
|---|---|
| CustomField | makeCustomFieldUnitEditable(unit, customFieldName) |
| 状态 | makeStateUnitEditable(unit) |
| 日期 | makeDateUnitEditable(unit, propertyName) |
支持的 CustomField 单元类型:
- 文本
- NUMBER
- 金钱
- 日期
- 下拉菜单
- 多项列表
模型类型
单元模型可以是字符串:model: 'due:CustomValues.Date("Due Date"),closed:(entityState.isFinal or entityState.Name="Cancelled")'并且也反对:model: {
due:'CustomValues.Date("Due Date")',
closed:'(entityState.isFinal or entityState.Name="Cancelled")'
}