open_lark/card/components/interactive_components/
checker.rs

1use serde::{Deserialize, Serialize};
2
3use crate::card::{
4    components::{
5        content_components::plain_text::PlainText, interactive_components::input::InputConfirm,
6    },
7    icon::FeishuCardTextIcon,
8    interactions::Behaviors,
9};
10
11/// 勾选器
12#[derive(Debug, Serialize, Deserialize)]
13pub struct Checker {
14    /// 组件的标签。勾选器组件的固定值为 checker。
15    tag: String,
16    /// 勾选器组件的唯一标识。用于识别用户提交的数据属于哪个组件。
17    ///
18    /// 注意:当勾选器组件嵌套在表单容器中时,该字段必填且需在卡片全局内唯一。
19    #[serde(skip_serializing_if = "Option::is_none")]
20    name: Option<String>,
21    /// 勾选器的初始勾选状态。可选值:
22    ///
23    /// - true:已勾选状态
24    /// - false:未勾选状态
25    #[serde(skip_serializing_if = "Option::is_none")]
26    checked: Option<bool>,
27    /// 勾选器组件内的普通文本信息。
28    #[serde(skip_serializing_if = "Option::is_none")]
29    text: Option<PlainText>,
30    /// 当光标悬浮在勾选器上时,勾选器整体是否有阴影效果。
31    ///
32    /// 注意:要取消阴影效果,你需确保 overall_checkable 为 false 且 pc_display_rule 不为
33    /// on_hover。
34    #[serde(skip_serializing_if = "Option::is_none")]
35    overall_checkable: Option<bool>,
36    /// 按钮区配置。
37    #[serde(skip_serializing_if = "Option::is_none")]
38    button_area: Option<ButtonArea>,
39    /// 勾选状态样式。
40    #[serde(skip_serializing_if = "Option::is_none")]
41    checked_style: Option<CheckedStyle>,
42    /// 组件整体的外边距,支持填写单值或多值:
43    ///
44    /// - 单值:如 "4px",表示组件的四个外边距都为 4px
45    /// - 多值:如 "4px 12px 4px 12px",表示容器内上、右、下、左的内边距分别为
46    ///   4px,12px,4px,12px。四个值必填,使用空格间隔
47    #[serde(skip_serializing_if = "Option::is_none")]
48    margin: Option<String>,
49    /// 组件整体的内边距,支持填写单值或多值:
50    ///
51    /// - 单值:如 "4px",表示组件内四个内边距都为 4px
52    /// - 多值:如 "4px 12px 4px 12px",表示容器内上、右、下、左的内边距分别为
53    ///   4px,12px,4px,12px。四个值必填,使用空格间隔
54    #[serde(skip_serializing_if = "Option::is_none")]
55    padding: Option<String>,
56    /// 二次确认弹窗配置。指在用户提交时弹出二次确认弹窗提示;只有用户点击确认后,
57    /// 才提交输入的内容。该字段默认提供了确认和取消按钮,你只需要配置弹窗的标题与内容即可。
58    ///
59    /// 注意:confirm 字段仅在用户点击包含提交属性的按钮时才会触发二次确认弹窗。
60    #[serde(skip_serializing_if = "Option::is_none")]
61    confirm: Option<InputConfirm>,
62    /// 配置交互类型和具体交互行为。未配置 behaviors 时,终端用户可勾选,但仅本地有效。
63    behaviors: Vec<Behaviors>,
64    /// 用户在 PC 端将光标悬浮在勾选器上方时的文案提醒。
65    ///
66    /// 注意:当同时配置 hover_tips 和 disabled_tips 时,disabled_tips 将生效。
67    #[serde(skip_serializing_if = "Option::is_none")]
68    hover_tips: Option<PlainText>,
69    /// 是否禁用该勾选器。可选值:
70    ///
71    /// - true:禁用
72    /// - false:勾选器组件保持可用状态
73    #[serde(skip_serializing_if = "Option::is_none")]
74    disabled: Option<bool>,
75    /// 禁用勾选器后,用户在 PC 端将光标悬浮在勾选器上方时的文案提醒。s
76    #[serde(skip_serializing_if = "Option::is_none")]
77    disabled_tips: Option<PlainText>,
78}
79
80impl Default for Checker {
81    fn default() -> Self {
82        Self {
83            tag: "checker".to_string(),
84            name: None,
85            checked: None,
86            text: None,
87            overall_checkable: None,
88            button_area: None,
89            checked_style: None,
90            margin: None,
91            padding: None,
92            confirm: None,
93            behaviors: vec![],
94            hover_tips: None,
95            disabled: None,
96            disabled_tips: None,
97        }
98    }
99}
100
101impl Checker {
102    pub fn new() -> Self {
103        Self::default()
104    }
105
106    pub fn name(mut self, name: &str) -> Self {
107        self.name = Some(name.to_string());
108        self
109    }
110
111    pub fn checked(mut self, checked: bool) -> Self {
112        self.checked = Some(checked);
113        self
114    }
115
116    pub fn text(mut self, text: PlainText) -> Self {
117        self.text = Some(text);
118        self
119    }
120
121    pub fn overall_checkable(mut self, overall_checkable: bool) -> Self {
122        self.overall_checkable = Some(overall_checkable);
123        self
124    }
125
126    pub fn button_area(mut self, button_area: ButtonArea) -> Self {
127        self.button_area = Some(button_area);
128        self
129    }
130
131    pub fn checked_style(mut self, checked_style: CheckedStyle) -> Self {
132        self.checked_style = Some(checked_style);
133        self
134    }
135
136    pub fn margin(mut self, margin: &str) -> Self {
137        self.margin = Some(margin.to_string());
138        self
139    }
140
141    pub fn padding(mut self, padding: &str) -> Self {
142        self.padding = Some(padding.to_string());
143        self
144    }
145
146    pub fn confirm(mut self, confirm: InputConfirm) -> Self {
147        self.confirm = Some(confirm);
148        self
149    }
150
151    pub fn behaviors(mut self, behaviors: Vec<Behaviors>) -> Self {
152        self.behaviors = behaviors;
153        self
154    }
155
156    pub fn hover_tips(mut self, hover_tips: PlainText) -> Self {
157        self.hover_tips = Some(hover_tips);
158        self
159    }
160
161    pub fn disabled(mut self, disabled: bool) -> Self {
162        self.disabled = Some(disabled);
163        self
164    }
165
166    pub fn disabled_tips(mut self, disabled_tips: PlainText) -> Self {
167        self.disabled_tips = Some(disabled_tips);
168        self
169    }
170}
171
172/// 按钮区配置
173#[derive(Debug, Serialize, Deserialize, Default)]
174pub struct ButtonArea {
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pc_display_rule: Option<String>,
177    #[serde(skip_serializing_if = "Option::is_none")]
178    buttons: Option<Vec<Buttons>>,
179}
180
181impl ButtonArea {
182    pub fn new() -> Self {
183        Self {
184            pc_display_rule: None,
185            buttons: None,
186        }
187    }
188
189    pub fn pc_display_rule(mut self, pc_display_rule: &str) -> Self {
190        self.pc_display_rule = Some(pc_display_rule.to_string());
191        self
192    }
193
194    pub fn buttons(mut self, buttons: Vec<Buttons>) -> Self {
195        self.buttons = Some(buttons);
196        self
197    }
198}
199
200/// 勾选器配置按钮
201#[derive(Debug, Serialize, Deserialize)]
202pub struct Buttons {
203    /// 按钮的标签,取固定值 button
204    tag: String,
205    /// 按钮的类型,可选值:
206    ///
207    /// - text:黑色字体按钮,无边框
208    /// - primary_text:蓝色字体按钮,无边框
209    /// - danger_text:红色字体按钮,无边框
210    r#type: String,
211    /// 按钮的尺寸,可选值:
212    ///
213    /// - tiny:超小尺寸,PC 端为 24px;移动端为 28px
214    /// - small:小尺寸,PC 端为 28 px;移动端为 28 px
215    /// - medium:中尺寸,PC 端为 32 px;移动端为 36 px
216    /// - large:大尺寸,PC 端为 40 px;移动端为 48 px
217    #[serde(skip_serializing_if = "Option::is_none")]
218    size: Option<String>,
219    /// 按钮上的文本。
220    #[serde(skip_serializing_if = "Option::is_none")]
221    text: Option<PlainText>,
222    /// 添加图标作为文本前缀图标。支持自定义或使用图标库中的图标。
223    #[serde(skip_serializing_if = "Option::is_none")]
224    icon: Option<FeishuCardTextIcon>,
225    /// 是否禁用按钮。可选值:
226    ///
227    /// - true:禁用按钮
228    /// - false:按钮组件保持可用状态
229    #[serde(skip_serializing_if = "Option::is_none")]
230    disabled: Option<bool>,
231    /// 配置交互类型和具体交互行为
232    behaviors: Vec<Behaviors>,
233}
234
235impl Buttons {
236    pub fn new(r#type: &str) -> Self {
237        Self {
238            tag: "button".to_string(),
239            r#type: r#type.to_string(),
240            size: None,
241            text: None,
242            icon: None,
243            disabled: None,
244            behaviors: vec![],
245        }
246    }
247
248    pub fn r#type(mut self, r#type: &str) -> Self {
249        self.r#type = r#type.to_string();
250        self
251    }
252
253    pub fn size(mut self, size: &str) -> Self {
254        self.size = Some(size.to_string());
255        self
256    }
257
258    pub fn text(mut self, text: PlainText) -> Self {
259        self.text = Some(text);
260        self
261    }
262
263    pub fn icon(mut self, icon: FeishuCardTextIcon) -> Self {
264        self.icon = Some(icon);
265        self
266    }
267
268    pub fn disabled(mut self, disabled: bool) -> Self {
269        self.disabled = Some(disabled);
270        self
271    }
272
273    pub fn behaviors(mut self, behaviors: Vec<Behaviors>) -> Self {
274        self.behaviors = behaviors;
275        self
276    }
277}
278
279#[derive(Debug, Serialize, Deserialize, Default)]
280pub struct CheckedStyle {
281    /// 是否展示内容区的贯穿式删除线。
282    show_strikethrough: Option<bool>,
283    /// 内容区的不透明度。取值范围为 [0,1] 之间的数字,不限小数位数。
284    opacity: Option<f32>,
285}
286
287impl CheckedStyle {
288    pub fn new() -> Self {
289        Self::default()
290    }
291
292    pub fn show_strikethrough(mut self, show_strikethrough: bool) -> Self {
293        self.show_strikethrough = Some(show_strikethrough);
294        self
295    }
296
297    pub fn opacity(mut self, opacity: f32) -> Self {
298        self.opacity = Some(opacity);
299        self
300    }
301}
302
303#[cfg(test)]
304mod test {
305    use serde_json::json;
306
307    use crate::card::interactions::CallbackBehavior;
308
309    use super::*;
310
311    #[test]
312    fn test_checker() {
313        let checker = super::Checker::new()
314            .name("check_1")
315            .checked(false)
316            .text(
317                PlainText::text("")
318                    .text_size("normal")
319                    .text_color("default")
320                    .text_align("left"),
321            )
322            .overall_checkable(true)
323            .button_area(ButtonArea::new().pc_display_rule("always").buttons(
324                vec![Buttons::new("text")
325                    .size("small")
326                    .text(PlainText::text("text按钮"))
327                    .icon(
328                        FeishuCardTextIcon::new()
329                            .token("chat-forbidden_outlined")
330                            .color("orange")
331                            .img_key("img_v2_38811724"),
332                    ).disabled(false)],
333            ))
334            .checked_style(CheckedStyle::new().show_strikethrough(true).opacity(1.0))
335            .margin("0px")
336            .padding("0px")
337            .behaviors(vec![Behaviors::Callback(CallbackBehavior::new(
338                json!({"key": "value"}),
339            ))])
340            .disabled(false);
341
342        let json = json!({
343          "tag": "checker",  // 组件的标签。勾选器组件的固定值为 checker。
344          "name": "check_1",  // 勾选器组件的唯一标识。用于识别用户提交的数据属于哪个组件。
345          "checked": false,  // 勾选器的初始勾选状态。默认值 false。
346          "text": {  // 勾选器组件内的普通文本信息。
347            "tag": "plain_text", // 文本类型的标签。
348            "content": "", // 文本的内容。当 tag 为 lark_md 时,支持部分 Markdown 语法的文本内容。
349            "text_size": "normal", // 文本大小。默认值 normal。
350            "text_color": "default", // 文本颜色。仅在 tag 为 plain_text 时生效。默认值 default。
351            "text_align": "left", // 文本对齐方式。默认值 left。
352          },
353          "overall_checkable": true,  // 当光标悬浮在勾选器上时,勾选器整体是否有阴影效果。默认值 true。
354          "button_area": {  // 按钮区的配置。可选。
355            "pc_display_rule": "always",   // PC 端勾选器内按钮的展示规则。默认值 always,即始终显示按钮。
356            "buttons": [  // 在勾选器中添加并配置按钮。最多可配置三个按钮。
357              {
358                "tag": "button",  // 按钮的标签,取固定值 button。
359                "type": "text",  // 按钮的类型。必填。
360                "size": "small", // 按钮的尺寸。默认值 medium。
361                "text": {   // 按钮上的文本。
362                  "tag": "plain_text",
363                  "content": "text按钮"
364                },
365                "icon": {   // 添加图标作为按钮文本上的前缀图标。支持自定义或使用图标库中的图标。
366                  "tag": "standard_icon", // 图标类型。
367                  "token": "chat-forbidden_outlined", // 图标的 token。仅在 tag 为 standard_icon 时生效。
368                  "color": "orange", // 图标颜色。仅在 tag 为 standard_icon 时生效。
369                  "img_key": "img_v2_38811724" // 图片的 key。仅在 tag 为 custom_icon 时生效。
370                },
371                "disabled": false,
372                "behaviors": []
373              }
374            ]
375          },
376          "checked_style": {  // 勾选状态样式。
377            "show_strikethrough": true,  // 是否展示内容区的贯穿式删除线。默认值 false。
378            "opacity": 1.0  // 内容区的不透明度。默认值 1。
379          },
380          "margin": "0px",  // 组件整体的外边距,支持填写单值或多值。默认值为 0px。
381          "padding": "0px",  // 组件整体的内边距,支持填写单值或多值。默认值为 0px。
382
383
384          "behaviors": [  // 配置交互类型和具体交互行为。未配置 behaviors 时,终端用户可勾选,但仅本地有效。
385            {
386              "type": "callback", // 声明交互类型。仅支持 callback 请求回调交互。
387              "value": {
388                // 回传交互数据
389                "key": "value"
390              }
391            }
392          ],
393
394          "disabled": false,  // 是否禁用该勾选器。默认值 false。
395
396        });
397
398        assert_eq!(json!(checker), json);
399    }
400}