Excel、Jira、Rest API 端到端示例
关于如何将 Jira REST API 与 Excel 集成的端到端视图。
引言
网上有很多关于 Jira REST API 的信息,并且可以从 Excel 对 Jira 进行一些简单的 REST 调用,但是我一直未能找到一个好的端到端资源,可以将所有概念整合在一起,向人们展示如何登录、检索数据、解析返回数据并插入到 Excel 中。有一些第三方付费的 Excel 插件,但我想要一个通用的 Jira 与 Excel 集成插件,它能让我获取、更新、创建,并从 Jira 信息创建 Word 文档(即更改请求表单),而无需付费。
在本文中,我将分享我的经验、Excel VBA 代码,以及对其他代码的引用,这些代码提供了 Jira、Excel 和 Word 之间相当优雅(尽管仍在进行中)的半通用集成。
背景
使用 Jira 时,我想要解决的第一个问题是,我希望看到一个问题列表,以及它们的关联问题、关联问题的当前状态和已修复版本。我无法在 Jira 中找到实现此功能的方法,因此我决定编写一个 Excel 宏,该宏将从 Jira 获取问题,并解析关联的工单并列出状态和已修复版本,以便在一行/视图中查看工单及其关联工单的状态。在下图的 O、P 和 Q 列中显示了我正在寻找的结果。如果您查看 CLOUD-8050 行,您将看到当有多个问题链接到一个问题时,它是如何显示在一个单元格中的。
解决了这个问题之后,我不断扩展 Excel 宏的功能,以创建新工单、更新工单并使用 Jira 工单中的数据填充 Word 文档。创建和更新功能或多或少是一次性需求,但表单项目似乎是一个持续的通用问题。
在从事这个项目时,我最大的问题是,有很多文章讨论了在 Excel 中使用 Jira Rest API 的单点解决方案,但没有真正提供关于如何登录、调用 API、解析 Json 以及我所学到的细微差别的端到端视图。
使用 Excel Jira 集成宏
在本节中,我将记录如何以最终用户身份使用 Excel 工作簿。在兴趣点部分,我将记录学习内容和关键代码片段。
本文附带的 Excel 工作簿包含自定义菜单添加项以及我开发或其他人开发的宏。
打开工作簿时,功能区上会有一个名为 JIRA 的新菜单。这是一个自定义菜单,它调用执行与检索和处理 Jira 数据相关的各种功能的宏。
主要的菜单选项是“获取问题”,如果用户尚未通过 Excel 登录 Jira,此宏将调用登录。它将在 B1 列中执行 Jira JQL 命令,并从 A8 处的表中返回结果,该表在 Excel 中由定义的名称“StartRow”引用。
请参见背景部分图片中 Excel 工作簿的示例。
在 macroValues 工作表上有一个表,用于定义如何从 JSON 格式的 Jira 返回数据中提取数据,以便将其插入到 Excel 工作表中。
在 Excel 工作表“工单”的第 5 行(已隐藏)包含每个列的键。此键会在 macroValues 工作表中的此表中查找信息,以确定如何从 Jira Json 中提取数据,以及是否应将方法应用于数据以转换数据。
您可以通过简单地插入新列并指明要用于转换数据的键值,从而添加和删除“工单”工作表中的列。您可以通过遵循表中的格式,在 macroValues 中更新您的 Jira 实例特定自定义字段和您可能想要拉入 Excel 的其他字段。请注意,即使您的字段名称/标签相同,自定义字段也会有不同的值。这些值对于每个 Jira 实例都是唯一的。
当您对表进行更改时,需要单击功能区 Jira 组中的“初始化”按钮。这将动态创建一个名为 getValue 的宏,并使用此表创建一个子例程,该子例程知道如何引用 Jira 返回数据中的所需数据。
字段 | 值参考 | 检查空引用 | 方法 |
---|---|---|---|
键 | ("key") | ("key") | |
状态 | ("fields")("status")("name") | ("fields")("status")("name") | |
项目 | ("fields")("project")("name") | ("fields")("project")("name") | |
summary | ("fields")("summary") | ("fields")("summary") | |
已创建 | ("fields")("created") | ("fields")("created") | 从ISODateTimeNoZ |
已更新 | ("fields")("updated") | ("fields")("updated") | 从ISODateTimeNoZ |
受让人 | ("fields")("assignee")("name") | ("fields")("assignee") | |
报告人 | ("fields")("reporter")("name") | ("fields")("reporter") | |
修复版本 | ("fields")("fixversions") | ("fields")("fixversions") | 获取带计数字段 |
问题类型 | ("fields")("issuetype")("name") | ("fields")("issuetype") | |
标签 | ("fields")("labels") | ("fields")("labels") | 获取带计数字段 |
优先级 | ("fields")("priority")("name") | ("fields")("priority") | |
严重性 | ("fields")("customfield_12520")("value") | ("fields")("customfield_12520")("value") | |
解决 | ("fields")("resolution")("name") | ("fields")("resolution") | |
问题链接 | ("fields")("issuelinks") | ("fields")("issuelinks") | 获取字段问题链接 |
业务驱动 | ("fields")("customfield_15623") | ("fields")("customfield_15623") | |
业务重要性 | ("fields")("customfield_12420") | ("fields")("customfield_12420") | |
description | ("fields")("description") | ("fields")("description") | |
详细描述 | ("fields")("customfield_15621") | ("fields")("customfield_15621") | |
可计费 | ("fields")("customfield_15926")(1)("value") | ("fields")("customfield_15926") | |
工作量估算 | ("fields")("customfield_15629") | ("fields")("customfield_15629") | |
需要审批 | ("fields")("customfield_16320")("value") | ("fields")("customfield_16320") | |
经批准者 | ("fields")("customfield_11622")("displayName") | ("fields")("customfield_11622") |





工具和依赖项
在进入兴趣点并讨论一些代码之前。已经使用了许多工具和其他代码。因此,在此向应得的致敬...
- Chrome Postman
- 我发现使用 Chrome 插件 Postman 非常有价值,因为我可以确保 REST URI 的基本格式和结构是正确的,并在 Postman 中对其进行测试。查看返回的 Json 也非常有用,这样我就可以识别层次结构,知道如何访问转换后的 Json 对象结构。
- 通过 Postman 将 REST API 字符串发送到 Jira,我可以看到返回的原始 JSON。这是确定自定义字段标签的最佳方法,以便您可以准确地解引用返回的 JSON。您可能需要使用一些唯一的标识值更新现有工单,以便在返回的 JSON 中挑出该字段。
- 保存用于登录对话框的用户名和服务器 URL,它使用了我从以下位置获取的 WriteProp 和 ReadProp 方法。
http://word.mvps.org/faqs/macrosvba/MixedDocProps.htm
- Microsoft Office 自定义 UI 编辑器
- 我在 http://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2009/08/07/7293.aspx 找到了这个工具,并利用它轻松自定义 Excel 功能区菜单。
- 为了将我自己的菜单添加到 Excel 文档中,我使用 UI 编辑器工具编辑和更新 Excel 文件,以便在打开 Excel 文件时可以使用 JIRA 功能区菜单。
- 有用的东西
VBA 项目中有几个方法以“有用的东西”开头。有关“有用的东西”的详细信息,请查看以下网站。我复制了 fromISODateTime 函数并创建了 fromISODateTimeNoZ,它只是从正则表达式中删除了“Z”,以便解析从 Jira 返回的日期,这些日期是 ISO 格式,但末尾没有 Z。我曾一度尝试使用 mcpher 代码中提供的 cJobject,但最终使用的是下面提到的 VBA-JSON 项目。ramblings.mcpher.com 上似乎有很多很酷的东西,值得一读。我导入了很多他的代码,但最终没有使用太多。无论如何,我认为那里有一些非常酷的概念和东西值得进一步研究。
有用信息材料的许可和版权信息->
选项明确
' v2.23 3414346
这个工具相对容易使用,但我没有找到太多关于其实际用法的文档,并且不得不通过反复试验学习很多东西。我遇到的最大问题是确定是否存在类似 IsNULL 的值。
' ---
- 这个工具相对容易使用,但我没有找到太多关于其实际用法的文档,并且不得不通过反复试验学习很多东西。我遇到的最大问题是确定是否存在类似 IsNULL 的值。
- VBA-JSON 工具相对容易使用。
Set JsonObject = JsonConverter.ParseJson(JsonIssues)
这里,从 Jira 返回的 Json 字符串被解析成一个名为 JsonObject 的对象,解析成对象后,您可以将 Json 引用为数组类型对象的数组。
从上面 Chrome Postman 中返回的 Json 图像中,可以进行以下引用。
dim s as string s = JsonObject("maxResults") s = JsonObject("issues")(1)("key") s = JsonObject("issues")(1)("fields")("customfield_16730")("value") s = JsonObject("issues")(1)("fields")("priority")("name")
这里的诀窍是,当一个字段有子数组时,您必须检查父区域是否为空。您不能使用 isnull(JsonObject("issues")(1)("fields")("priority")("name")),您必须检查 JsonObject("issues")(1)("fields")("priority") 是否为空。
有用信息材料的许可和版权信息->
' VBA-JSON v2.0.1
' (c) Tim Hall - https://github.com/VBA-tools/VBA-JSON
'
' VBA 的 JSON 转换器
'
' 错误
' 10001 - JSON 解析错误
'
' @class JsonConverter
' @author tim.hall.engr@gmail.com
' @license MIT (https://open-source.org.cn/licenses/mit-license.php)
'' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '
'
' 最初基于 vba-json(经过大量修改)
' 包含以下 BSD 许可证
'
' JSONLib, http://code.google.com/p/vba-json/
'
' 版权所有 (c) 2013, Ryo Yokoyama
' 保留所有权利。
关注点
基本代码流程
- 登录 Jira
- 调用 Jira REST API 返回 Json
- 将 Json 转换为对象
- 使用 Excel 中定义的表解引用对象
- 将结果插入工作表或 Word 文档
登录
互联网上有很多登录 Jira 的例子。为了完善这些例子,我增加了一些功能。
- 登录对话框
- 能够在基本身份验证和无身份验证之间选择
- 保存用户名和 Jira URL 以备将来登录
- 在同一会话中保存凭据,以便不需要后续登录
凭据存储在一个名为 usernamep 的全局变量中,如果该值不为空“”,则在进行登录调用时,该值将与先前提供的凭据一起返回。否则,将显示一个对话框。如果选中“无身份验证”复选框,则 usernamep 将设置为“NoAuth”。
提供 GetIssues 函数以显示如何使用登录信息。当您将其组合在一起时,它非常基本。如您所见,如果它是 NoAuth,则标头设置为“Authorization”、“No Auth”,否则您将标头设置为 basic 并使用编码的用户名密码。
将 usernamep 用于两个目的是一种糟糕的编程实践,理想情况下,授权值应该是一个完全独立的变量。未来的代码版本可能会支持这一点,以便可以支持额外的授权方法。
If usernamep = "NoAuth" Then
.SetRequestHeader "Authorization", "No Auth"
Else
.SetRequestHeader "Authorization", "Basic " & usernamep
End If
Public usernamep As String Dim url As String Dim noauth As Boolean Public Function UserPassBase64() As String Dim objXML As MSXML2.DOMDocument60 Dim objNode As MSXML2.IXMLDOMElement Dim arrData() As Byte If usernamep = "" Then fLogin.Show If (fLogin.Cancel = False) Then usernamep = fLogin.txtUsername.Value + ":" + fLogin.txtPassword.Value url = fLogin.txtServer.Value noauth = fLogin.cbNoAuth.Value If noauth = True Then UserPassBase64 = "NoAuth" usernamep = "NoAuth" Else arrData = StrConv(usernamep, vbFromUnicode) Set objXML = New MSXML2.DOMDocument60 Set objNode = objXML.createElement("b64") objNode.DataType = "bin.base64" objNode.nodeTypedValue = arrData UserPassBase64 = objNode.Text End If Else UserPassBase64 = "cancel" End If Else UserPassBase64 = usernamep End If End Function
Rest 调用以获取问题
Public Function GetIssues(query As String) As String Dim JiraService As New MSXML2.XMLHTTP60 Dim json As Object Dim s As String usernamep = UserPassBase64 If usernamep = "cancel" Then fStatus.AppendStatus ("User canceled login.") Else fStatus.AppendStatus ("Getting Jira Issues you might see Not Responding in title bar. The time this takes is dependant on the complexity of your query, network, and number of issues being returned.") With JiraService s = GetUrl + "/rest/api/2/search?jql=" + query + "&startAt=" + _ CStr(Range("StartAt").Value) + "&maxResults=" + _ CStr(Range("MaxResults").Value) .Open "GET", s .SetRequestHeader "Content-Type", "application/json" .SetRequestHeader "Accept", "application/json" If usernamep = "NoAuth" Then .SetRequestHeader "Authorization", "No Auth" Else .SetRequestHeader "Authorization", "Basic " & usernamep End If .Send "" If .status = "401" Then fStatus.AppendStatus ("Something wrong with query in GetIssues, check your network connection, : " + .ResponseText) GetIssues = "" Else GetIssues = JiraService.ResponseText End If End With End If End Function
将 Json 转换为对象并插入工作表
这里更有趣的方法是,一旦 Json 从 Jira 返回,代码就会遍历返回 Json 中的行和工作表中的列。使用列标题提取要显示的数据。
此方法的基本流程是
- 删除表中的现有数据
- 将 Json 字符串转换为对象:设置 JsonObject = JsonConverter.ParseJson(JsonIssues)
- 循环行,遍历 Json 中返回的问题数量
- 循环列并将数据插入工作表
Sub ProcessIssues(JsonIssues As String) Dim t As ListObject Dim z As Double Dim JsonObject As Object Dim r As Range Dim fpath As Range Dim s As String Set t = ActiveWorkbook.Sheets("Tickets").ListObjects("Table2") ' ignoring error if table is already empty On Error Resume Next t.DataBodyRange.Delete On Error GoTo 0 Set r = Range("StartRow") ' row to start putting data in Set fpath = Range("StartFieldKey") ' Column headings to loop through 'Convert Json to an object array that makes it easy to access the Json Hierarchy fStatus.AppendStatus ("Parsing Returned Json") Set JsonObject = JsonConverter.ParseJson(JsonIssues) Range("TotalReturned").Value = JsonObject("total") fStatus.AppendStatus ("Retrieved " & CStr(JsonObject("total")) & " issue(s) matching query from Jira. Max issues to be retrieved is set at " & CStr(Range("MaxResults").Value) & vbCrLf & " Update maxResults on Trickets sheet to retrieve more.") Set issues = JsonObject("issues") On Error GoTo gerr For z = 1 To issues.count ' row loop: through and process each issue fStatus.AppendStatusBar ("Inserting jira issue " & z & " of " & issues.count & " into worksheet.") fStatus.progress ((z * 100 / (issues.count)) / 2) While (fpath.Value <> "") ' column loop: for each column in excel get the value from the Json Object and put in the cell ' Select statement first handles fields that have to have special handling these are fields that have multiple values usually ' the case else : calls the default field handling where there is a simple translaction from the Json On Error Resume Next r.Offset(0, fpath.column - 1).Value = getValue(issues(z), fpath.Value) On Error GoTo gerr Set fpath = fpath.Offset(0, 1) Wend Set fpath = Range("StartFieldKey") Set r = r.Offset(1, 0) Next z GoTo finish gerr: fStatus.AppendStatus ("Oops something went wrong: " & vbCrLf & err.description & vbCrLf & " source: " & err.Source & " at row: " & r.row & " column: " & fpath.Value) finish: End Sub
获取值
getValue 函数是根据 macroValues 工作表上 D 到 G 列中的表格动态创建的。这允许能够根据每个 Jira 实例的自定义字段值从 Json 中提取数据,因为自定义字段值对于 Jira 实例是唯一的。
CreateCaseSub 函数创建一个新函数,并使用 VBProject 对象引用将其动态添加到 Excel VBA 项目中。
创建函数的函数具有创建长字符串然后调用适当方法将字符串作为代码插入的基本形式。
- 创建函数头代码
- 遍历表格并创建 Select Case 语句
- 创建函数脚注代码
- 插入到 VBA 项目中
Function CreateCaseSub() As VBComponent Dim code As String Dim r As Range Set r = Range("getValueStart") code = "Public Function getValue(issue as object, key as String) as String" & vbNewLine & _ vbTab & "dim rvalue as String" & vbNewLine & _ vbTab & "on error goto gerr" & vbNewLine & _ vbTab & "Select Case key" & vbNewLine While (r.Value <> "") code = code & vbTab & vbTab & "Case " & """" & r.Offset(0, 0).Value & """" & vbNewLine & _ vbTab & vbTab & vbTab & "if IsNull(issue" & r.Offset(0, 2).Value & ") then" & vbNewLine & _ vbTab & vbTab & vbTab & vbTab & "rvalue = """"" & vbNewLine & _ vbTab & vbTab & vbTab & "else" & vbNewLine If r.Offset(0, 3).Value = "" Then code = code & vbTab & vbTab & vbTab & vbTab & "rvalue = issue" & r.Offset(0, 1).Value & vbNewLine Else code = code & vbTab & vbTab & vbTab & vbTab & "rvalue = " & r.Offset(0, 3).Value & "(issue" & r.Offset(0, 1).Value & ")" & vbNewLine End If code = code & vbTab & vbTab & vbTab & "End if" & vbNewLine Set r = r.Offset(1, 0) Wend code = code & vbTab & "End Select" & vbNewLine & _ vbTab & "getValue = rvalue" & vbNewLine & _ vbTab & "goto finish:" & vbNewLine & _ "gerr:" & vbNewLine & _ vbTab & "AppendStatus (""error getValue issue key: "" & key & "" "" & err.description )" & vbNewLine & _ "finish:" & vbNewLine & _ "End Function" Dim tempModule As VBComponent Set tempModule = ThisWorkbook.VBProject.VBComponents.add(VBIDE.vbext_ComponentType.vbext_ct_StdModule) Call tempModule.codeModule.DeleteLines(1, tempModule.codeModule.CountOfLines) Call tempModule.codeModule.AddFromString(code) tempModule.name = "Module1" Set CreateCaseSub = tempModule End Function
以下代码是 getValue 的一个示例。对于表中的每个键,都会检查对象是否存在空引用,并返回“”(表示空)或实际的对象数据。
最初我使用了 iif 函数,但它和许多三元运算符一样,实际上会解析 false 表达式并在无法引用对象时抛出错误,因此必须使用更冗长的 if then else 子句。
Public Function getValue(issue As Object, key As String) As String Dim rvalue As String On Error GoTo gerr Select Case key Case "key" If IsNull(issue("key")) Then rvalue = "" Else rvalue = issue("key") End If Case "status" If IsNull(issue("fields")("status")("name")) Then rvalue = "" Else rvalue = issue("fields")("status")("name") End If Case "project" If IsNull(issue("fields")("project")("name")) Then rvalue = "" Else rvalue = issue("fields")("project")("name") End If Case "summary" If IsNull(issue("fields")("summary")) Then rvalue = "" Else rvalue = issue("fields")("summary") End If Case "created" If IsNull(issue("fields")("created")) Then rvalue = "" Else rvalue = fromISODateTimeNoZ(issue("fields")("created")) End If Case "updated" If IsNull(issue("fields")("updated")) Then rvalue = "" Else rvalue = fromISODateTimeNoZ(issue("fields")("updated")) End If Case "assignee" If IsNull(issue("fields")("assignee")) Then rvalue = "" Else rvalue = issue("fields")("assignee")("name") End If Case "reporter" If IsNull(issue("fields")("reporter")) Then rvalue = "" Else rvalue = issue("fields")("reporter")("name") End If Case "fixversions" If IsNull(issue("fields")("fixversions")) Then rvalue = "" Else rvalue = GetFieldsWithCount(issue("fields")("fixversions")) End If Case "issuetype" If IsNull(issue("fields")("issuetype")) Then rvalue = "" Else rvalue = issue("fields")("issuetype")("name") End If Case "labels" If IsNull(issue("fields")("labels")) Then rvalue = "" Else rvalue = GetFieldsWithCount(issue("fields")("labels")) End If Case "prioity" If IsNull(issue("fields")("priority")) Then rvalue = "" Else rvalue = issue("fields")("priority")("name") End If Case "severity" If IsNull(issue("fields")("customfield_12520")("value")) Then rvalue = "" Else rvalue = issue("fields")("customfield_12520")("value") End If Case "resolution" If IsNull(issue("fields")("resolution")) Then rvalue = "" Else rvalue = issue("fields")("resolution")("name") End If Case "issuelinks" If IsNull(issue("fields")("issuelinks")) Then rvalue = "" Else rvalue = GetFieldsIssueLink(issue("fields")("issuelinks")) End If Case "businessDriver" If IsNull(issue("fields")("customfield_15623")) Then rvalue = "" Else rvalue = issue("fields")("customfield_15623") End If Case "businessSignificance" If IsNull(issue("fields")("customfield_12420")) Then rvalue = "" Else rvalue = issue("fields")("customfield_12420") End If Case "description" If IsNull(issue("fields")("description")) Then rvalue = "" Else rvalue = issue("fields")("description") End If Case "detailedDescription" If IsNull(issue("fields")("customfield_15621")) Then rvalue = "" Else rvalue = issue("fields")("customfield_15621") End If Case "billable" If IsNull(issue("fields")("customfield_15926")) Then rvalue = "" Else rvalue = issue("fields")("customfield_15926")(1)("value") End If Case "LOE" If IsNull(issue("fields")("customfield_15629")) Then rvalue = "" Else rvalue = issue("fields")("customfield_15629") End If Case "approvalRequired" If IsNull(issue("fields")("customfield_16320")) Then rvalue = "" Else rvalue = issue("fields")("customfield_16320")("value") End If Case "approvedBy" If IsNull(issue("fields")("customfield_11622")) Then rvalue = "" Else rvalue = issue("fields")("customfield_11622")("displayName") End If End Select getValue = rvalue GoTo finish: gerr: AppendStatus ("error getValue issue key: " & key & " " & err.description) finish: End FunctionPublic Function
将数据插入 Word
将代码插入 Word 的方式类似。它也使用 getValue 将存储在 Word 自定义表单字段标记属性中的键值转换为用于插入 Word 模板的数据。创建 Word 模板的基本流程是
- 选择 Word 模板并提供 Jira 票证密钥
- 打开 Word 模板
- 检索该票证的 Jira 数据
- 遍历自定义表单字段
- 将标签与键值匹配并插入数据
以下代码执行解析。在提示输入模板和键,检索 Json 并将其转换为对象后,此代码会遍历 Word 表单字段。
Sub FilloutWordTemplateWithJiraData(ByRef o As Object, template As String, s As fEnhancement) Dim w As Word.Application Dim wd As Word.Document Dim jiraKey As String Dim oCC As ContentControl Dim i As Integer Dim lc As Boolean s.status.Caption = "Opening Microsoft Word" Set w = CreateObject("Word.Application") w.Visible = True s.status.Caption = "Opening template: " & template Set wd = w.Documents.Open(template) 'wd.SaveAs MyDocuments() & "\" & jiraKey i = 1 For Each oCC In wd.ContentControls strText = "" s.status.Caption = "Update form field " & i & " of " & wd.ContentControls.count i = i + 1 Debug.Print oCC.tag strText = getValue(o, oCC.tag) lc = oCC.LockContents oCC.LockContents = False Select Case oCC.Type Case wdContentControlCheckBox If strText = "Yes" Then oCC.Checked = True End If 'Case wdContentControlDate ' todo: determine if special date handling needs to be added Case default If strText <> "" Then oCC.Range.InsertAfter strText oCC.SetPlaceholderText , , strText ' if placeholder text exist this removes it so that a clean document without unwanted texted is provided. End If End Select oCC.LockContents = lc Next oCC s.status.Caption = "Finished updating " & i & " fields in template." Exit Sub End Sub
解析 Jira Json 的经验
可计费 | ("fields")("customfield_15926")(1)("value") | ("fields")("customfield_15926") |
... "customfield_13624": null, "customfield_15926": [ { "self": "https://jira.mycompany.com/rest/api/2/customFieldOption/16883", "value": "Yes", "id": "16883" } ], "cus....
创建 Excel 菜单
为每个 Excel 文件创建自定义菜单非常酷。请注意,这不是永久性的加载项。菜单只会在您打开关联的 Excel 文件并使其获得焦点时显示。
要使用 Microsoft Office 的自定义 UI 编辑器,只需在工具中打开 Excel 文件(注意:Excel 文件不能在 Excel 中打开),创建 XML 并保存文件。如果 XML 中存在错误,菜单将不会显示,也不会显示错误消息。因此,建议一次添加一个元素。
我没有尝试为我的自定义菜单创建自定义图标,而是从 Office 的标准图标集中选择。我利用以下链接中的参考资料浏览了可用的图标。
http://soltechs.net/CustomUI/imageMso01.asp
我发现的一个更令人恼火的学习是,UI 编辑器似乎除了菜单外,还将 Excel 宏加载到其内存中。因此,如果您打开 UI 编辑器,打开 Excel 文件并更新 XML,然后保存它,但让 Excel 文件在 UI 编辑器中保持打开状态,如果您回到 UI 编辑器并进行更改并再次保存,您对 Excel 宏所做的任何更改都将被覆盖。因此,学习是:在 UI 编辑器中打开 Excel 文件,进行更改,然后关闭文件,再在 Excel 中打开。
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<ribbon>
<tabs>
<tab id="customTab" label="JIRA" insertAfterMso="TabView">
<group idMso="GroupClipboard" />
<group idMso="GroupFont" />
<group id="customGroup" label="Jira Tools">
<button id="customButton5" label="Login" size="large" onAction="Login" imageMso="TableIndexes" />
<button id="customButton4" label="Get Issues" size="large" onAction="GetTickets" imageMso="CacheListData" />
<button id="customButton1" label="Get Linked Status" size="large" onAction="UpdateStatus" imageMso="AccessRelinkLists" />
<button id="customButton3" label="Create Tickets" size="large" onAction="CreateTickets" imageMso="QueryShowTable" />
<button id="customButton2" label="Update Dashboard" size="large" onAction="UpdateDashboard" imageMso="CreateForm"/>
<button id="customButton7" label="Create Enhancement" size="large" onAction="CreateEForm" imageMso="ColumnsDialog"/>
</group>
</tab>
<group idMso="GroupEnterDataAlignment" />
<group idMso="GroupEnterDataNumber" />
<group idMso="GroupQuickFormatting" />
</tabs>
</ribbon>
</customUI>
随机思考
起初,我使用 Excel 左下角的 Excel 状态栏并将 Excel 光标更新为 xlWait,但后来意识到状态栏有点偏僻,不那么引人注目。所以我弹出一个带有状态标签的对话框,向用户提供进度反馈。这似乎更人性化,信息量也更大。我还决定不更改光标,因为对话框会阻止用户执行任何操作,直到宏完成并提供所需的视觉反馈。我发现,当您尝试手动更新光标时,几乎总会出现不可预见的情况,导致光标处于不准确的状态。
在字符串前后与 vbCrLf 和逗号进行了大量的尝试,才使“链接状态”和“链接已修复版本”在单元格内与关联的链接工单对齐。可能有一种更好的处理方法,但目前这种字符串操作的暴力方法奏效了。
历史
2016年4月13日:编辑文章,额外致谢 http://word.mvps.org/faqs/macrosvba/MixedDocProps.htm
更新了附件 Excel 工作簿,包含了 GetIssues 中的修复,将 .send "" 仅改为 .send,如 Dirk_B 所报告