Files
dingtalk-workspace-cli/internal/helpers/sheet_cond_format.go

409 lines
19 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package helpers
import (
"encoding/json"
"fmt"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
"github.com/spf13/cobra"
)
func newCondFormatCmd() *cobra.Command {
condFormatCmd := &cobra.Command{Use: "cond-format", Short: "条件格式管理"}
condFormatListCmd := &cobra.Command{
Use: "list",
Short: "获取条件格式规则",
Long: `获取钉钉电子表格中指定工作表的所有条件格式规则,或通过 --rule-id 获取单个规则的详情。
条件格式规则定义了当单元格满足特定条件时自动应用的样式(如背景色、字体颜色等)。
返回的每条规则包含:规则 ID、应用范围、条件类型及参数、命中时的单元格样式。
通过 --node 定位电子表格文档,通过 --sheet-id 定位工作表。
nodeId 支持传入文档链接 URL 或文档 ID(dentryUuid),系统自动识别。
sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获取。`,
Example: ` # 获取所有条件格式规则
dws sheet cond-format list --node NODE_ID --sheet-id SHEET_ID
# 获取单个规则的详情
dws sheet cond-format list --node NODE_ID --sheet-id SHEET_ID --rule-id RULE_ID`,
RunE: func(cmd *cobra.Command, args []string) error {
toolArgs := map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
}
if v, _ := cmd.Flags().GetString("rule-id"); v != "" {
toolArgs["ruleId"] = v
}
return callMCPTool("get_cond_format", toolArgs)
},
}
DeclareLeafMetadata(condFormatListCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "read", Risk: "low",
Confirmation: "not_required", Idempotency: "idempotent",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "get_cond_format",
CanonicalPath: "sheet.get_cond_format",
CLIPath: "sheet cond-format list",
PrimaryCLIPath: "sheet cond-format list",
},
Description: "列出工作表条件格式规则。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "get_cond_format"},
},
Selection: contract.SelectionSpec{
AgentSummary: "列出工作表条件格式规则。",
UseWhen: []string{"查看或修改前需要枚举条件格式规则 ID 时"},
AvoidWhen: []string{"创建/更新/删除分别用 cond-format create/update/delete"},
Examples: []string{"dws sheet cond-format list --node <NODE_ID> --sheet-id <SHEET_ID>"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
},
},
})
condFormatListCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
condFormatListCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
condFormatListCmd.Flags().String("rule-id", "", "条件格式规则 ID (可选,不传则返回全部)")
condFormatCreateCmd := &cobra.Command{
Use: "create",
Short: "创建条件格式规则",
Long: `在钉钉电子表格的指定工作表中创建一条条件格式规则。
条件格式规则定义了当单元格满足特定条件时自动应用的样式。
每条规则需要指定:应用范围(--ranges)、条件类型及参数(--condition)、命中时的单元格样式(--cell-style)。
通过 --node 定位电子表格文档,通过 --sheet-id 定位工作表。
nodeId 支持传入文档链接 URL 或文档 ID(dentryUuid),系统自动识别。
sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获取。
--ranges 为 JSON 数组,A1 表示法,如 '["A1:E10"]'。
--condition 为 JSON 对象,包含条件类型及参数。每条规则只能选择一种条件类型。
--cell-style 为 JSON 对象,指定条件命中时的样式。
--data-bar-style 为 JSON 对象,仅数据条类型时使用。
支持的条件类型(condition JSON 的 key,每次只能选一种):
numberCondition 数值比较
operator: equal/not-equal/greater/greater-equal/less/less-equal/between/not-between
value1: 比较值(字符串,必填)
value2: 第二比较值(字符串,仅 between/not-between 时必填)
textCondition 文本匹配
operator: contains/not-contains/starts-with/ends-with
value: 匹配文本
emptyCondition 空值判断(operator: is-empty/is-not-empty)
errorCondition 错误值判断(operator: error/no-error)
duplicateCondition 重复/唯一值(operator: duplicate/unique)
formulaCondition 自定义公式(formula: "=A1>100")
rankCondition 排名(value: 数量, isPercent: 是否百分比, isBottom: 是否倒数)
averageCondition 高于/低于平均值(isAbove: 是否高于, andEqual: 是否含等于)
stdevCondition 标准差(value: 倍数, isAbove: 方向, andEqual: 是否含等于)
dataBarCondition 数据条
minPoint/maxPoint: { type: auto/maxmin/number/percent/percentile/formula, value: 端点值 }
iconSetCondition 图标集
iconSet: 数组,每项含 criteria({ type, value, gtOrEqual }) 和 icon({ type: "id", value: 图标ID })
showIconOnly: 是否仅显示图标
colorScaleCondition 色阶
criterias: 数组(2或3项),每项含 { type: maxmin/number/percent/percentile/formula, value, color }
--cell-style JSON 字段:
backgroundColor 背景色(十六进制如 #FFCDD2)
fontColor 字体颜色(十六进制如 #B71C1C)
bold 是否加粗(布尔)
italic 是否斜体(布尔)
strikethrough 是否删除线(布尔)
--data-bar-style JSON 字段(仅 dataBarCondition 时使用):
fill 填充颜色数组 [正数颜色, 负数颜色],如 ["#4CAF50","#F44336"]
isGradient 是否渐变填充(布尔,默认 false)
注意事项:
- 创建后必须用 cond-format list 验证规则是否生效
- 中文"标红/高亮/染色"默认指 backgroundColor,"字体红"才是 fontColor
- 日期/空值公式必须防空:=AND(E1<>"", E1<=TODAY()) 而非 =E1<=TODAY()
- 公式中用相对引用(如 =E1<=TODAY())使公式随行变化,绝对引用只比较一个格
- 创建前建议先 range read 读 3-5 行数据确认列对应关系`,
Example: ` # 数值条件:大于 80 时标红
dws sheet cond-format create --node NODE_ID --sheet-id SHEET_ID \
--ranges '["A1:A100"]' \
--condition '{"numberCondition":{"operator":"greater","value1":"80"}}' \
--cell-style '{"backgroundColor":"#FFCDD2","fontColor":"#B71C1C","bold":true}'
# 文本条件:包含"延期"时加删除线
dws sheet cond-format create --node NODE_ID --sheet-id SHEET_ID \
--ranges '["B1:B50"]' \
--condition '{"textCondition":{"operator":"contains","value":"延期"}}' \
--cell-style '{"backgroundColor":"#FFF3E0","strikethrough":true}'
# 数据条
dws sheet cond-format create --node NODE_ID --sheet-id SHEET_ID \
--ranges '["C1:C20"]' \
--condition '{"dataBarCondition":{"minPoint":{"type":"auto"},"maxPoint":{"type":"auto"}}}' \
--data-bar-style '{"fill":["#4CAF50","#F44336"],"isGradient":true}'
# 色阶(三色)
dws sheet cond-format create --node NODE_ID --sheet-id SHEET_ID \
--ranges '["D1:D50"]' \
--condition '{"colorScaleCondition":{"criterias":[{"type":"maxmin","color":"#F44336"},{"type":"percentile","value":"50","color":"#FFEB3B"},{"type":"maxmin","color":"#4CAF50"}]}}'`,
RunE: func(cmd *cobra.Command, args []string) error {
rangesStr := mustGetFlag(cmd, "ranges")
var ranges []string
if err := json.Unmarshal([]byte(rangesStr), &ranges); err != nil {
return fmt.Errorf("--ranges JSON 解析失败: %w", err)
}
if len(ranges) == 0 {
return fmt.Errorf("--ranges 至少包含 1 个范围")
}
conditionStr := mustGetFlag(cmd, "condition")
var condition map[string]any
if err := json.Unmarshal([]byte(conditionStr), &condition); err != nil {
return fmt.Errorf("--condition JSON 解析失败: %w", err)
}
toolArgs := map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
"ranges": ranges,
}
for key, val := range condition {
toolArgs[key] = val
}
if cellStyleStr, _ := cmd.Flags().GetString("cell-style"); cellStyleStr != "" {
var cellStyle map[string]any
if err := json.Unmarshal([]byte(cellStyleStr), &cellStyle); err != nil {
return fmt.Errorf("--cell-style JSON 解析失败: %w", err)
}
toolArgs["cellStyle"] = cellStyle
}
if dataBarStyleStr, _ := cmd.Flags().GetString("data-bar-style"); dataBarStyleStr != "" {
var dataBarStyle map[string]any
if err := json.Unmarshal([]byte(dataBarStyleStr), &dataBarStyle); err != nil {
return fmt.Errorf("--data-bar-style JSON 解析失败: %w", err)
}
toolArgs["dataBarStyle"] = dataBarStyle
}
return callMCPTool("create_cond_format", toolArgs)
},
}
DeclareLeafMetadata(condFormatCreateCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "create_cond_format",
CanonicalPath: "sheet.create_cond_format",
CLIPath: "sheet cond-format create",
PrimaryCLIPath: "sheet cond-format create",
},
Description: "创建条件格式规则。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "create_cond_format"},
},
Selection: contract.SelectionSpec{
AgentSummary: "创建条件格式规则。",
UseWhen: []string{"需要按条件自动着色/标注单元格时"},
AvoidWhen: []string{"改已有规则用 update;删规则用 delete;普通样式刷用 set-style"},
Examples: []string{"dws sheet cond-format create --node NODE_ID --sheet-id SHEET_ID --ranges '[\"A1:E10\"]' --condition '{\"numberCondition\":{\"operator\":\"greater\",\"value1\":\"80\"}}' --cell-style '{\"backgroundColor\":\"#FFEB3B\"}'"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
},
},
})
condFormatCreateCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
condFormatCreateCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
condFormatCreateCmd.Flags().String("ranges", "", `应用范围 JSON 数组 (必填),如 '["A1:E10"]'`)
condFormatCreateCmd.Flags().String("condition", "", `条件类型及参数 JSON 对象 (必填),如 '{"numberCondition":{"operator":"greater","value1":"80"}}'`)
condFormatCreateCmd.Flags().String("cell-style", "", `单元格样式 JSON 对象 (可选),如 '{"backgroundColor":"#FF0000","bold":true}'`)
condFormatCreateCmd.Flags().String("data-bar-style", "", `数据条样式 JSON 对象 (可选,仅数据条类型),如 '{"fill":["#4CAF50","#F44336"],"isGradient":true}'`)
condFormatUpdateCmd := &cobra.Command{
Use: "update",
Short: "更新条件格式规则",
Long: `更新钉钉电子表格中指定工作表的一条条件格式规则。
可以部分更新以下字段:
- --ranges:修改规则的应用范围
- --condition:切换或修改条件类型及参数
- --cell-style:修改命中时的单元格样式
- --data-bar-style:修改数据条样式
未传入的字段将保持原有值不变。
传入 --condition 时,新条件类型将替换原有条件类型。
通过 --node 定位电子表格文档,通过 --sheet-id 定位工作表,通过 --rule-id 定位条件格式规则。
nodeId 支持传入文档链接 URL 或文档 ID(dentryUuid),系统自动识别。
sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获取。
ruleId 可通过 cond-format list 获取。`,
Example: ` # 修改规则的条件(改为大于 90)
dws sheet cond-format update --node NODE_ID --sheet-id SHEET_ID --rule-id RULE_ID \
--condition '{"numberCondition":{"operator":"greater","value1":"90"}}'
# 修改规则的样式
dws sheet cond-format update --node NODE_ID --sheet-id SHEET_ID --rule-id RULE_ID \
--cell-style '{"backgroundColor":"#C8E6C9","fontColor":"#1B5E20"}'
# 修改规则的应用范围
dws sheet cond-format update --node NODE_ID --sheet-id SHEET_ID --rule-id RULE_ID \
--ranges '["A1:F200"]'`,
RunE: func(cmd *cobra.Command, args []string) error {
toolArgs := map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
"ruleId": mustGetFlag(cmd, "rule-id"),
}
rangesChanged := cmd.Flags().Changed("ranges")
conditionChanged := cmd.Flags().Changed("condition")
cellStyleChanged := cmd.Flags().Changed("cell-style")
dataBarStyleChanged := cmd.Flags().Changed("data-bar-style")
if !rangesChanged && !conditionChanged && !cellStyleChanged && !dataBarStyleChanged {
return fmt.Errorf("--ranges、--condition、--cell-style、--data-bar-style 至少需要传入一个")
}
if rangesChanged {
rangesStr, _ := cmd.Flags().GetString("ranges")
var ranges []string
if err := json.Unmarshal([]byte(rangesStr), &ranges); err != nil {
return fmt.Errorf("--ranges JSON 解析失败: %w", err)
}
toolArgs["ranges"] = ranges
}
if conditionChanged {
conditionStr, _ := cmd.Flags().GetString("condition")
var condition map[string]any
if err := json.Unmarshal([]byte(conditionStr), &condition); err != nil {
return fmt.Errorf("--condition JSON 解析失败: %w", err)
}
for key, val := range condition {
toolArgs[key] = val
}
}
if cellStyleChanged {
cellStyleStr, _ := cmd.Flags().GetString("cell-style")
var cellStyle map[string]any
if err := json.Unmarshal([]byte(cellStyleStr), &cellStyle); err != nil {
return fmt.Errorf("--cell-style JSON 解析失败: %w", err)
}
toolArgs["cellStyle"] = cellStyle
}
if dataBarStyleChanged {
dataBarStyleStr, _ := cmd.Flags().GetString("data-bar-style")
var dataBarStyle map[string]any
if err := json.Unmarshal([]byte(dataBarStyleStr), &dataBarStyle); err != nil {
return fmt.Errorf("--data-bar-style JSON 解析失败: %w", err)
}
toolArgs["dataBarStyle"] = dataBarStyle
}
return callMCPTool("update_cond_format", toolArgs)
},
}
DeclareLeafMetadata(condFormatUpdateCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "update_cond_format",
CanonicalPath: "sheet.update_cond_format",
CLIPath: "sheet cond-format update",
PrimaryCLIPath: "sheet cond-format update",
},
Description: "更新已有条件格式规则。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "update_cond_format"},
},
Selection: contract.SelectionSpec{
AgentSummary: "更新已有条件格式规则。",
UseWhen: []string{"已知规则 ID,需要调整条件或样式时"},
AvoidWhen: []string{"新建用 create;删除用 delete"},
Examples: []string{"dws sheet cond-format update --node NODE_ID --sheet-id SHEET_ID --rule-id RULE_ID --condition '{\"numberCondition\":{\"operator\":\"greater\",\"value1\":\"90\"}}'"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
},
},
})
condFormatUpdateCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
condFormatUpdateCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
condFormatUpdateCmd.Flags().String("rule-id", "", "条件格式规则 ID (必填)")
condFormatUpdateCmd.Flags().String("ranges", "", `应用范围 JSON 数组 (可选),如 '["A1:E10"]'`)
condFormatUpdateCmd.Flags().String("condition", "", `条件类型及参数 JSON 对象 (可选),传入后替换原有条件类型`)
condFormatUpdateCmd.Flags().String("cell-style", "", `单元格样式 JSON 对象 (可选)`)
condFormatUpdateCmd.Flags().String("data-bar-style", "", `数据条样式 JSON 对象 (可选,仅数据条类型)`)
condFormatDeleteCmd := &cobra.Command{
Use: "delete",
Short: "删除条件格式规则",
Long: `删除钉钉电子表格中指定工作表的一条条件格式规则。
通过 --node 定位电子表格文档,通过 --sheet-id 定位工作表,通过 --rule-id 定位要删除的规则。
删除成功后该规则将从工作表中移除,被该规则影响的单元格将不再应用对应的条件格式样式。
如果规则已不存在,操作仍然返回成功。
⚠️ 危险操作:删除不可恢复。必须先向用户确认,用户同意后才加 --yes 执行。
nodeId 支持传入文档链接 URL 或文档 ID(dentryUuid),系统自动识别。
sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获取。
ruleId 可通过 cond-format list 获取。`,
Example: ` # 删除条件格式规则(必须加 --yes 确认)
dws sheet cond-format delete --node NODE_ID --sheet-id SHEET_ID --rule-id RULE_ID --yes`,
RunE: func(cmd *cobra.Command, args []string) error {
return callMCPTool("delete_cond_format", map[string]any{
"nodeId": mustGetFlag(cmd, "node"),
"sheetId": mustGetFlag(cmd, "sheet-id"),
"ruleId": mustGetFlag(cmd, "rule-id"),
})
},
}
DeclareLeafMetadata(condFormatDeleteCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "destructive", Risk: "high",
Confirmation: "user_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
ProductID: "sheet",
Name: "delete_cond_format",
CanonicalPath: "sheet.delete_cond_format",
CLIPath: "sheet cond-format delete",
PrimaryCLIPath: "sheet cond-format delete",
},
Description: "删除条件格式规则(需确认后加 --yes)。",
Interface: &contract.InterfaceSpec{
Mode: "mcp",
Availability: "available",
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "delete_cond_format"},
},
Selection: contract.SelectionSpec{
AgentSummary: "删除条件格式规则(需确认后加 --yes)。",
UseWhen: []string{"用户明确要求删除某条条件格式规则时"},
AvoidWhen: []string{"只想改规则用 update;列目录用 list"},
Examples: []string{"dws sheet cond-format delete --node NODE_ID --sheet-id SHEET_ID --rule-id RULE_ID"},
},
Parameters: []contract.ParamDecl{
{Name: "node", Property: "nodeId"},
},
},
})
condFormatDeleteCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
condFormatDeleteCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
condFormatDeleteCmd.Flags().String("rule-id", "", "条件格式规则 ID (必填)")
condFormatDeleteCmd.Flags().Bool("yes", false, "确认删除(危险操作,必须用户同意后才加此标志)")
condFormatCmd.AddCommand(condFormatListCmd, condFormatCreateCmd, condFormatUpdateCmd, condFormatDeleteCmd)
return condFormatCmd
}