使用下拉菜单条状标签

借助 Google Docs API,您可以以程序化方式在 Google 文档中创建、插入、更新、读取和管理下拉菜单条状标签。

什么是下拉菜单条状标签?

Google 文档中的下拉菜单条状标签可为用户提供一个交互式、可自定义的选择菜单,该菜单会内嵌在文档文本中。用户可以点击下拉菜单条状标签,从预定义的选项列表中进行选择,每个选项都有自己的显示文本和颜色样式。下拉菜单条状标签经常用于项目跟踪、状态更新、审核工作流和审批阶段。

通过 Docs API,您可以:

  • 定义可重复使用的下拉菜单模板,其中包含自定义的标题、选项名称和颜色。
  • 在有效字符位置插入下拉条状标签。
  • 更新单个下拉菜单条状标签实例的所选选项。
  • 就地修改共享下拉菜单定义,同时更新所有引用芯片。
  • 安全地替换或停用选项,同时在现有芯片中保持引用完整性。
  • 删除未使用的下拉菜单模板。

架构:定义和实例

在 Docs API 中,下拉菜单条状标签具有定义和实例。定义用于为下拉菜单实例设置选项。下拉菜单实例是指用户可以与之互动的下拉菜单,它会保存有关所选内容的信息。

Docs API 将下拉菜单模板配置与内嵌下拉菜单条状标签实例分开:

  1. DropdownDefinition:一种标签页级模板,用于定义下拉菜单标题和可供选择的选项集合 (DropdownOption)。它不位于文档中的特定字符偏移位置,而是存储在标签页的定义映射中:document.tabs[].documentTab.dropdownDefinitions。
  2. 下拉菜单:嵌入在段落元素 (ParagraphElement.dropdown) 中的单个 chip 实例。每个下拉菜单实例都引用一个 dropdownDefinitionId 并存储自己的有效 selectedOptionId。

修改 DropdownDefinition(例如,添加选项或重命名标题)会更新整个标签页中所有引用该 的 chip,而无需更新每个单独的元素。

每个 Dropdown 实例都会跟踪自己的 selectedOptionId。更改单个 chip 的所选值只会影响该特定实例。

ID 格式和验证规则

用户提供的 ID 必须严格遵守格式、前缀和长度规范:

标识符 强制性前缀 用于验证的正则表达式 长度限制 示例
下拉菜单定义 ID (dropdownDefinitionId) kix. ^kix\.[a-zA-Z0-9_-]{2,14}$ 6-18 个字符 kix.review_status
下拉选项 ID (optionId) dropdownItem. ^dropdownItem\.[a-zA-Z0-9_-]{2,14}$ 15-27 个字符 dropdownItem.pending

生成用户提供的 ID

如需生成符合强制性前缀和正则表达式要求的 ID,请使用以下辅助函数。这些辅助函数会生成小写 base-36 字母数字后缀,与 Google 文档界面生成的格式一致:

Python

import random
import string

def generate_dropdown_definition_id(suffix_len: int = 8) -> str:
    """Generates a valid user-provided DropdownDefinition ID (6-18 chars, starting with 'kix.')."""
    chars = string.ascii_lowercase + string.digits
    suffix = "".join(random.choices(chars, k=max(2, min(suffix_len, 14))))
    return f"kix.{suffix}"

def generate_dropdown_option_id(suffix_len: int = 8) -> str:
    """Generates a valid user-provided DropdownOption ID (15-27 chars, starting with 'dropdownItem.')."""
    chars = string.ascii_lowercase + string.digits
    suffix = "".join(random.choices(chars, k=max(2, min(suffix_len, 14))))
    return f"dropdownItem.{suffix}"

Java

import java.security.SecureRandom;

public final class DropdownIdGenerator {
  private static final String BASE36_CHARS =
      "abcdefghijklmnopqrstuvwxyz0123456789";
  private static final SecureRandom RANDOM = new SecureRandom();

  private DropdownIdGenerator() {}

  /** Generates a valid user-provided DropdownDefinition ID (6-18 chars, starting with "kix."). */
  public static String generateDefinitionId(int suffixLength) {
    int length = Math.max(2, Math.min(suffixLength, 14));
    StringBuilder sb = new StringBuilder("kix.");
    for (int i = 0; i < length; i++) {
      sb.append(BASE36_CHARS.charAt(RANDOM.nextInt(BASE36_CHARS.length())));
    }
    return sb.toString();
  }

  /** Generates a valid user-provided DropdownOption ID (15-27 chars, starting with "dropdownItem."). */
  public static String generateOptionId(int suffixLength) {
    int length = Math.max(2, Math.min(suffixLength, 14));
    StringBuilder sb = new StringBuilder("dropdownItem.");
    for (int i = 0; i < length; i++) {
      sb.append(BASE36_CHARS.charAt(RANDOM.nextInt(BASE36_CHARS.length())));
    }
    return sb.toString();
  }
}

创建和插入下拉菜单条状标签

单批次创建(推荐)

最有效的模式是在一次 documents.batchUpdate 调用中同时使用 CreateDropdownDefinitionRequest 和 InsertDropdownRequest。

以下代码示例使用用户提供的 ID 创建了一个包含三个彩色编码选项的“审核状态”下拉菜单定义,并在文档末尾插入了一个实例:

Python

requests = [
    {
        "createDropdownDefinition": {
            "dropdownDefinition": {
                "dropdownDefinitionId": "kix.review_status",
                "dropdownDefinitionProperties": {
                    "title": "Review Status",
                    "options": [
                        {
                            "optionId": "dropdownItem.pending",
                            "displayValue": "Pending Review",
                            "textStyle": {
                                "backgroundColor": {
                                    "color": {"rgbColor": {"red": 0.99, "green": 0.90, "blue": 0.65}}
                                },
                                "foregroundColor": {
                                    "color": {"rgbColor": {"red": 0.45, "green": 0.30, "blue": 0.0}}
                                },
                            },
                        },
                        {
                            "optionId": "dropdownItem.approved",
                            "displayValue": "Approved",
                            "textStyle": {
                                "backgroundColor": {
                                    "color": {"rgbColor": {"red": 0.85, "green": 0.95, "blue": 0.85}}
                                },
                                "foregroundColor": {
                                    "color": {"rgbColor": {"red": 0.08, "green": 0.40, "blue": 0.15}}
                                },
                            },
                        },
                        {
                            "optionId": "dropdownItem.rejected",
                            "displayValue": "Needs Changes",
                            "textStyle": {
                                "backgroundColor": {
                                    "color": {"rgbColor": {"red": 0.98, "green": 0.84, "blue": 0.84}}
                                },
                                "foregroundColor": {
                                    "color": {"rgbColor": {"red": 0.65, "green": 0.10, "blue": 0.10}}
                                },
                            },
                        },
                    ],
                },
            }
        }
    },
    {
        "insertDropdown": {
            "endOfSegmentLocation": {},
            "dropdownDefinitionId": "kix.review_status",
            "selectedOptionId": "dropdownItem.pending",
        }
    },
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<Request> requests = new ArrayList<>();

List<DropdownOption> options = Arrays.asList(
    new DropdownOption()
        .setOptionId("dropdownItem.pending")
        .setDisplayValue("Pending Review")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.99f).setGreen(0.90f).setBlue(0.65f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.45f).setGreen(0.30f).setBlue(0.0f))))),
    new DropdownOption()
        .setOptionId("dropdownItem.approved")
        .setDisplayValue("Approved")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.85f).setGreen(0.95f).setBlue(0.85f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.08f).setGreen(0.40f).setBlue(0.15f))))),
    new DropdownOption()
        .setOptionId("dropdownItem.rejected")
        .setDisplayValue("Needs Changes")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.98f).setGreen(0.84f).setBlue(0.84f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.65f).setGreen(0.10f).setBlue(0.10f))))));

DropdownDefinition definition = new DropdownDefinition()
    .setDropdownDefinitionId("kix.review_status")
    .setDropdownDefinitionProperties(new DropdownDefinitionProperties()
        .setTitle("Review Status")
        .setOptions(options));

requests.add(new Request().setCreateDropdownDefinition(
    new CreateDropdownDefinitionRequest().setDropdownDefinition(definition)));

requests.add(new Request().setInsertDropdown(
    new InsertDropdownRequest()
        .setEndOfSegmentLocation(new EndOfSegmentLocation())
        .setDropdownDefinitionId("kix.review_status")
        .setSelectedOptionId("dropdownItem.pending")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

下拉菜单验证规则和限制

创建或修改下拉菜单定义时,需要遵循以下限制:

  • 选项数量:下拉菜单定义必须包含介于 2 到 50 个选项。
  • 标题长度:定义 title 不得为空,且不得超过 200 个字符。
  • 显示值长度:每个选项的 displayValue 不能为空,且不得超过 200 个字符。
  • 选项样式:DropdownOption.textStyle 中仅支持 foregroundColor 和 backgroundColor。设置其他样式属性会返回 400 Bad Request 错误。
  • 默认选择:在 InsertDropdownRequest 中,如果省略 selectedOptionId,则 chip 默认采用 DropdownDefinition 中定义的第一个选项。

更新单个下拉菜单条状标签选择

如需更改现有下拉菜单条状标签实例的所选选项,而不更改其模板或其他条状标签,请使用 UpdateDropdownPropertiesRequest:

  • dropdownId:(必需)要更新的特定下拉菜单条状标签实例的 ID。
  • tabId:包含下拉菜单的标签页的 ID(如果省略,则默认为第一个标签页)。
  • fields:设置为 "selectedOptionId"。

以下代码示例将下拉菜单条状标签的有效选择项更新为 "dropdownItem.approved":

Python

requests = [
    {
        "updateDropdownProperties": {
            "dropdownId": "kix.chip_abc1",
            "tabId": "t.0",
            "dropdownProperties": {
                "selectedOptionId": "dropdownItem.approved",
            },
            "fields": "selectedOptionId",
        }
    }
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<Request> requests = new ArrayList<>();
requests.add(new Request().setUpdateDropdownProperties(
    new UpdateDropdownPropertiesRequest()
        .setDropdownId("kix.chip_abc1")
        .setTabId("t.0")
        .setDropdownProperties(new DropdownProperties()
            .setSelectedOptionId("dropdownItem.approved"))
        .setFields("selectedOptionId")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

更新下拉菜单定义和选项

如需修改模板本身(标题、选项列表、显示标签或颜色),请使用 UpdateDropdownDefinitionPropertiesRequest。更新定义后,相应标签页中引用该定义的所有下拉菜单条状标签都会自动更新。

替换选项的完整列表

如果 fields 掩码中包含 dropdownDefinitionProperties.options,则请求会执行完整列表替换。您必须按所选顺序提供完整的选项列表。服务器将传入的列表与当前定义进行比较:

  • 新选项:包含不带 optionId 的选项(或带有用户提供的新 optionId)会将其添加到定义中。
  • 更新选项:如果提供的现有 optionId 包含修改后的 displayValue 或 textStyle 更新,则会就地更新相应选项。
  • 重新排序的选项:选项列表会按照请求中提供的确切顺序保存。
  • 已删除的选项:省略现有 optionId 会从定义中删除相应选项。

参照完整性和选项替换

如果文档中的任何下拉菜单条状标签中选择了要删除的选项,您必须在 selectedOptionIdReplacements (map<string, string>) 中提供映射。键是要删除的选项的 ID,值是替换选项的 ID。

以下代码示例演示了如何通过替换选项来更新下拉菜单定义:

Python

requests = [
    {
        "updateDropdownDefinitionProperties": {
            "dropdownDefinitionId": "kix.review_status",
            "tabId": "t.0",
            "dropdownDefinitionProperties": {
                "title": "Editorial Review Status",
                "options": [
                    {
                        "optionId": "dropdownItem.approved",
                        "displayValue": "Approved",
                        "textStyle": {
                            "backgroundColor": {
                                "color": {"rgbColor": {"red": 0.85, "green": 0.95, "blue": 0.85}}
                            },
                            "foregroundColor": {
                                "color": {"rgbColor": {"red": 0.08, "green": 0.40, "blue": 0.15}}
                            },
                        },
                    },
                    {
                        "optionId": "dropdownItem.rejected",
                        "displayValue": "Changes Requested",
                        "textStyle": {
                            "backgroundColor": {
                                "color": {"rgbColor": {"red": 0.98, "green": 0.84, "blue": 0.84}}
                            },
                            "foregroundColor": {
                                "color": {"rgbColor": {"red": 0.65, "green": 0.10, "blue": 0.10}}
                            },
                        },
                    },
                ],
            },
            "selectedOptionIdReplacements": {
                "dropdownItem.pending": "dropdownItem.rejected"
            },
            "fields": "title,options",
        }
    }
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<DropdownOption> updatedOptions = Arrays.asList(
    new DropdownOption()
        .setOptionId("dropdownItem.approved")
        .setDisplayValue("Approved")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.85f).setGreen(0.95f).setBlue(0.85f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.08f).setGreen(0.40f).setBlue(0.15f))))),
    new DropdownOption()
        .setOptionId("dropdownItem.rejected")
        .setDisplayValue("Changes Requested")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.98f).setGreen(0.84f).setBlue(0.84f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.65f).setGreen(0.10f).setBlue(0.10f))))));

Map<String, String> replacements = new HashMap<>();
replacements.put("dropdownItem.pending", "dropdownItem.rejected");

List<Request> requests = new ArrayList<>();
requests.add(new Request().setUpdateDropdownDefinitionProperties(
    new UpdateDropdownDefinitionPropertiesRequest()
        .setDropdownDefinitionId("kix.review_status")
        .setTabId("t.0")
        .setDropdownDefinitionProperties(new DropdownDefinitionProperties()
            .setTitle("Editorial Review Status")
            .setOptions(updatedOptions))
        .setSelectedOptionIdReplacements(replacements)
        .setFields("title,options")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

删除下拉菜单定义

如需移除未使用的下拉菜单模板,请使用 DeleteDropdownDefinitionRequest。

以下代码示例演示了如何删除下拉菜单定义:

Python

requests = [
    {
        "deleteDropdownDefinition": {
            "dropdownDefinitionId": "kix.review_status",
            "tabId": "t.0",
        }
    }
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<Request> requests = new ArrayList<>();
requests.add(new Request().setDeleteDropdownDefinition(
    new DeleteDropdownDefinitionRequest()
        .setDropdownDefinitionId("kix.review_status")
        .setTabId("t.0")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

错误处理和问题排查

常见错误情况和解决方法:

状态代码 原因 分辨率
400 INVALID_ARGUMENT ID 前缀或正则表达式格式无效。 确保 dropdownDefinitionId 以 kix. 开头(6-18 个字符),且 optionId 以 dropdownItem. 开头(15-27 个字符)。
400 INVALID_ARGUMENT 选项数量超出范围。 下拉菜单定义必须包含 2 到 50 个选项。
400 INVALID_ARGUMENT 标题或显示值为空或超过 200 个字符。 提供一个介于 1 到 200 个字符之间的非空字符串。
400 INVALID_ARGUMENT 选项中存在不受支持的文本样式属性。 下拉选项仅支持 foregroundColor 和 backgroundColor。移除字体、大小或其他属性。
400 INVALID_ARGUMENT 删除时缺少选项替换映射。 删除任何条状标签所选的选项时,请在 selectedOptionIdReplacements 中提供有效的替换项。
400 INVALID_ARGUMENT 尝试删除正在使用的定义。 在删除下拉菜单条状标签定义之前,请先删除或重访定位引用该定义的所有下拉菜单条状标签实例。
400 INVALID_ARGUMENT 用户提供的 ID 重复。 确保用户提供的 ID 在文档标签页中是唯一的。