409 lines
19 KiB
Go
409 lines
19 KiB
Go
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
|
||
}
|