You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Chat Apps脚本:表单输入变更时不关闭对话框更新任务列表

解决Google Chat任务对话框下拉框变更时自动关闭的问题

问题核心

你遇到的问题根源是:Google Chat对话框中触发onChangeAction后,系统默认会关闭对话框。要实现变更后不关闭对话框并更新任务列表,关键是在action的响应中返回一个新的对话框实例,替换原有对话框,而非依赖系统默认行为。

解决方案步骤

1. 实现onStatusChange函数,返回更新后的对话框

当用户修改状态下拉框时,该函数需要完成:获取变更的任务ID与新状态、更新任务数据、重新生成带最新状态的对话框并返回。

function onStatusChange(event) {
  // 从输入组件名称中提取任务ID(例如"status_123" → "123")
  const taskId = event.commonWidgetSelection.inputName.replace("status_", "");
  // 获取选中的新状态值
  const newStatus = event.commonWidgetSelection.selectedValues[0];
  
  // 获取当前用户的任务列表并更新对应任务状态
  const userEmail = event.message.sender.email;
  let tasks = fetchOpenTasks(userEmail);
  tasks = tasks.map(task => {
    if (task.id === taskId) {
      return {...task, status: newStatus};
    }
    return task;
  });

  // 重新构建对话框(复用原有逻辑,后续可封装为公共函数)
  const statusIcons = {
    "Not Started": "🔴",
    "In Progress": "🟡",
    "Completed": "🟢"
  };

  const taskWidgets = tasks.map(task => ({
    widgets: [
      {
        decoratedText: {
          topLabel: task.title,
          text: task.description,
          wrapText : true,
          bottomLabel: `Due: ${task.dueDate}`,
        }
      },
      {
        columns: {
          columnItems: [
            {
              horizontalSizeStyle : "FILL_MINIMUM_SPACE",
              widgets: [
                {
                  selectionInput: {
                    name: `status_${task.id}`,
                    label: "Status",
                    type: "DROPDOWN",
                    onChangeAction: { actionMethodName: "onStatusChange" },
                    items: [
                      { text: `${statusIcons["Not Started"]} Not Started`, value: "Not Started", selected: task.status === "Not Started" },
                      { text: `${statusIcons["In Progress"]} In Progress`, value: "In Progress", selected: task.status === "In Progress" },
                      { text: `${statusIcons["Completed"]} Completed`, value: "Completed", selected: task.status === "Completed" }
                    ],
                    labelPosition: "TOP",
                    buttonStyle: "PRIMARY"
                  }
                }
              ]
            },
            {
              widgets:[
                {
                  textInput: {
                    name: `comments_${task.id}`,
                    label: "Add Comments",
                    type: "MULTIPLE_LINE",
                    value: task.comments || "",
                    // 给评论输入框添加onChangeAction,实现实时更新
                    onChangeAction: { actionMethodName: "onCommentChange" }
                  }
                }
              ]
            }
          ]
        }
      }
    ]
  }));

  // 返回更新后的对话框,核心是指定actionResponse类型为DIALOG
  return {
    actionResponse: {
      type: "DIALOG",
      dialogAction: {
        dialog: {
          body: {
            sections: [...taskWidgets],
            "fixedFooter": {
              "primaryButton": {
                "text": "Save changes",
                "color": {
                  "red": 0,
                  "green": 0.5,
                  "blue": 1,
                  "alpha": 1
                },
                "onClick": {
                  "action": {
                    "function": "updateTasks"
                  }
                }
              },
            }
          }
        }
      }
    }
  };
}

2. 实现评论输入框的onCommentChange函数

和状态变更逻辑一致,更新对应任务的评论内容后返回新对话框:

function onCommentChange(event) {
  const taskId = event.commonWidgetTextInput.inputName.replace("comments_", "");
  const newComment = event.commonWidgetTextInput.value;
  
  const userEmail = event.message.sender.email;
  let tasks = fetchOpenTasks(userEmail);
  tasks = tasks.map(task => {
    if (task.id === taskId) {
      return {...task, comments: newComment};
    }
    return task;
  });

  // 复用对话框构建逻辑(推荐封装为公共函数)
  return buildTaskDialog(tasks, userEmail);
}

3. 封装公共对话框构建函数(可选但推荐)

将重复的对话框构建逻辑抽离,减少冗余代码:

function buildTaskDialog(tasks, userEmail) {
  const statusIcons = {
    "Not Started": "🔴",
    "In Progress": "🟡",
    "Completed": "🟢"
  };

  const taskWidgets = tasks.map(task => ({
    widgets: [
      {
        decoratedText: {
          topLabel: task.title,
          text: task.description,
          wrapText : true,
          bottomLabel: `Due: ${task.dueDate}`,
        }
      },
      {
        columns: {
          columnItems: [
            {
              horizontalSizeStyle : "FILL_MINIMUM_SPACE",
              widgets: [
                {
                  selectionInput: {
                    name: `status_${task.id}`,
                    label: "Status",
                    type: "DROPDOWN",
                    onChangeAction: { actionMethodName: "onStatusChange" },
                    items: [
                      { text: `${statusIcons["Not Started"]} Not Started`, value: "Not Started", selected: task.status === "Not Started" },
                      { text: `${statusIcons["In Progress"]} In Progress`, value: "In Progress", selected: task.status === "In Progress" },
                      { text: `${statusIcons["Completed"]} Completed`, value: "Completed", selected: task.status === "Completed" }
                    ],
                    labelPosition: "TOP",
                    buttonStyle: "PRIMARY"
                  }
                }
              ]
            },
            {
              widgets:[
                {
                  textInput: {
                    name: `comments_${task.id}`,
                    label: "Add Comments",
                    type: "MULTIPLE_LINE",
                    value: task.comments || "",
                    onChangeAction: { actionMethodName: "onCommentChange" }
                  }
                }
              ]
            }
          ]
        }
      }
    ]
  }));

  return {
    actionResponse: {
      type: "DIALOG",
      dialogAction: {
        dialog: {
          body: {
            sections: [...taskWidgets],
            "fixedFooter": {
              "primaryButton": {
                "text": "Save changes",
                "color": {
                  "red": 0,
                  "green": 0.5,
                  "blue": 1,
                  "alpha": 1
                },
                "onClick": {
                  "action": {
                    "function": "updateTasks"
                  }
                }
              },
            }
          }
        }
      }
    }
  };
}

// 简化原showUpdateTaskDialog函数
function showUpdateTaskDialog(event) {
  const userEmail = event.message.sender.email;
  const tasks = fetchOpenTasks(userEmail);

  if (tasks.length === 0) {
    return {
      actionResponse: {
        type: "NEW_MESSAGE"
      },
      text: `You have no tasks assigned to you, ${event.message.sender.displayName}.`
    };
  }

  return buildTaskDialog(tasks, userEmail);
}

关键注意事项

  • 所有对话框内的变更action(onStatusChange、onCommentChange)必须返回actionResponse.type = "DIALOG",携带更新后的对话框结构,这样Google Chat会用新对话框替换旧的,而非关闭。
  • 确保在action中正确提取任务ID和新值,准确更新任务数据。
  • 封装公共构建函数可大幅降低代码冗余,提升维护效率。

内容的提问来源于stack exchange,提问作者PriyankaJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 18:09:52