open_lark/card/components/interactive_components/
select_static.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};
9
10/// 下拉选择-单选
11#[derive(Debug, Serialize, Deserialize)]
12pub struct SelectStatic {
13    /// 组件的标签。下拉选择-单选组件取固定值 select_static。
14    tag: String,
15    /// 组件边框样式。可选值:
16    ///
17    /// default:带边框样式
18    /// text:不带边框的纯文本样式
19    #[serde(skip_serializing_if = "Option::is_none")]
20    r#type: Option<String>,
21    /// 单选组件的唯一标识。当单选组件内嵌在表单容器时,该属性生效,
22    /// 用于识别用户提交的文本属于哪个单选组件。
23    ///
24    /// 注意:当单选组件嵌套在表单容器中时,该字段必填且需在卡片全局内唯一。
25    #[serde(skip_serializing_if = "Option::is_none")]
26    name: Option<String>,
27    /// 单选组件的内容是否必选。当组件内嵌在表单容器中时,该属性可用。其它情况将报错或不生效。
28    /// 可取值:
29    ///
30    /// - true:单选组件必选。当用户点击表单容器的“提交”时,未填写单选组件,
31    ///   则前端提示“有必填项未填写”,不会向开发者的服务端发起回传请求。
32    /// - false:单选组件选填。当用户点击表单容器的“提交”时,未填写单选组件,
33    ///   仍提交表单容器中的数据。
34    #[serde(skip_serializing_if = "Option::is_none")]
35    required: Option<bool>,
36    /// 是否禁用该单选组件。该属性仅支持飞书 V7.4 及以上版本的客户端。可选值:
37    ///
38    /// true:禁用单选组件组件
39    /// false:单选组件组件保持可用状态
40    #[serde(skip_serializing_if = "Option::is_none")]
41    disabled: Option<bool>,
42    /// 下拉选择组件的初始选项值。取值上限为选项的数量。该配置将会覆盖 placeholder 配置的占位文本。
43    #[serde(skip_serializing_if = "Option::is_none")]
44    initial_index: Option<u32>,
45    /// 下拉选择组件内的占位文本。
46    #[serde(skip_serializing_if = "Option::is_none")]
47    placeholder: Option<PlainText>,
48    /// 单选组件的宽度。支持以下枚举值:
49    ///
50    /// - default:默认宽度
51    /// - fill:卡片最大支持宽度
52    /// - [100,∞)px:自定义宽度。超出卡片宽度时将按最大支持宽度展示
53    #[serde(skip_serializing_if = "Option::is_none")]
54    width: Option<String>,
55    /// 选项的配置。
56    #[serde(skip_serializing_if = "Option::is_none")]
57    options: Option<Vec<SelectStaticOption>>,
58    /// 二次确认弹窗配置。指在用户提交时弹出二次确认弹窗提示;只有用户点击确认后,
59    /// 才提交输入的内容。该字段默认提供了确认和取消按钮,你只需要配置弹窗的标题与内容即可。
60    ///
61    /// 注意:confirm 字段仅在用户点击包含提交属性的按钮时才会触发二次确认弹窗。
62    #[serde(skip_serializing_if = "Option::is_none")]
63    confirm: Option<InputConfirm>,
64}
65
66impl Default for SelectStatic {
67    fn default() -> Self {
68        Self {
69            tag: "select_static".to_string(),
70            r#type: None,
71            name: None,
72            required: None,
73            disabled: None,
74            initial_index: None,
75            placeholder: None,
76            width: None,
77            options: None,
78            confirm: None,
79        }
80    }
81}
82
83impl SelectStatic {
84    pub fn new() -> Self {
85        Self::default()
86    }
87
88    pub fn r#type(mut self, r#type: &str) -> Self {
89        self.r#type = Some(r#type.to_string());
90        self
91    }
92
93    pub fn name(mut self, name: &str) -> Self {
94        self.name = Some(name.to_string());
95        self
96    }
97
98    pub fn required(mut self, required: bool) -> Self {
99        self.required = Some(required);
100        self
101    }
102
103    pub fn disabled(mut self, disabled: bool) -> Self {
104        self.disabled = Some(disabled);
105        self
106    }
107
108    pub fn initial_index(mut self, initial_index: u32) -> Self {
109        self.initial_index = Some(initial_index);
110        self
111    }
112
113    pub fn placeholder(mut self, placeholder: PlainText) -> Self {
114        self.placeholder = Some(placeholder);
115        self
116    }
117
118    pub fn width(mut self, width: &str) -> Self {
119        self.width = Some(width.to_string());
120        self
121    }
122
123    pub fn options(mut self, options: Vec<SelectStaticOption>) -> Self {
124        self.options = Some(options);
125        self
126    }
127
128    pub fn confirm(mut self, confirm: InputConfirm) -> Self {
129        self.confirm = Some(confirm);
130        self
131    }
132}
133
134/// 选项的配置。
135#[derive(Debug, Serialize, Deserialize, Default)]
136pub struct SelectStaticOption {
137    /// 选项的名称。
138    text: PlainText,
139    /// 添加图标作为文本前缀图标。支持自定义或使用图标库中的图标。
140    #[serde(skip_serializing_if = "Option::is_none")]
141    icon: Option<FeishuCardTextIcon>,
142    /// 自定义选项回调值。当用户点击交互组件的选项后,会将 value
143    /// 的值返回给接收回调数据的服务器。后续你可以通过服务器接收的 value 值进行业务处理。
144    ///
145    /// 注意:同一个选择组件内,各选项的 value 值不可重复,否则将无法识别用户点击的是哪个选项。
146    value: String,
147}
148
149impl SelectStaticOption {
150    pub fn new(text: &str, value: &str) -> Self {
151        Self {
152            text: PlainText::text(text),
153            icon: None,
154            value: value.to_string(),
155        }
156    }
157
158    pub fn text(mut self, text: PlainText) -> Self {
159        self.text = text;
160        self
161    }
162
163    pub fn icon(mut self, icon: FeishuCardTextIcon) -> Self {
164        self.icon = Some(icon);
165        self
166    }
167
168    pub fn value(mut self, value: &str) -> Self {
169        self.value = value.to_string();
170        self
171    }
172}
173
174#[cfg(test)]
175mod test {
176    use serde_json::json;
177
178    use crate::card::{
179        components::{
180            content_components::plain_text::PlainText,
181            interactive_components::{
182                input::InputConfirm,
183                select_static::{SelectStatic, SelectStaticOption},
184            },
185        },
186        icon::FeishuCardTextIcon,
187    };
188
189    #[test]
190    fn test_select_static() {
191        let select_static = SelectStatic::new()
192            .r#type("text")
193            .name("select_static1")
194            .required(false)
195            .disabled(false)
196            .initial_index(1)
197            .placeholder(PlainText::text("默认提示文本"))
198            .width("default")
199            .options(vec![SelectStaticOption::new("我是交互组件", "selectDemo1")
200                .icon(
201                    FeishuCardTextIcon::new()
202                        .token("chat-forbidden_outlined")
203                        .color("orange")
204                        .img_key("img_v2_38811724"),
205                )])
206            .confirm(InputConfirm::new("弹窗标题", "弹窗正文文案"));
207
208        let json = json!({
209          "tag": "select_static",  // 下拉选择-单选组件的标签。
210          "type": "text", // 组件边框样式。默认值 default。
211          "name": "select_static1", // 下拉选择-单选组件的唯一标识。当下拉选择-单选组件内嵌在表单容器时,该属性生效,用于识别用户提交的文本属于哪个下拉选择-单选组件。
212          "required": false, // 下拉选择-单选组件的内容是否必填。默认值 false。当下拉选择-单选组件内嵌在表单容器时,该属性可用。其它情况将报错或不生效。
213          "disabled": false, // 是否禁用该单选组件。默认值 false。
214          "initial_index": 1, // 选项展示的初始值。默认为空。
215          "placeholder": {
216            // 下拉选择组件内的占位文本。
217            "tag": "plain_text",
218            "content": "默认提示文本"
219          },
220          "width": "default",  // 下拉选择组件的宽度。
221          "options": [
222            // 选项配置
223            {
224              "text": {
225                // 选项名称
226                "tag": "plain_text",
227                "content": "我是交互组件"
228              },
229              "icon": {
230                // 添加图标作为选项前缀图标。支持自定义或使用图标库中的图标。
231                "tag": "standard_icon", // 图标类型。
232                "token": "chat-forbidden_outlined", // 图标的 token。仅在 tag 为 standard_icon 时生效。
233                "color": "orange", // 图标颜色。仅在 tag 为 standard_icon 时生效。
234                "img_key": "img_v2_38811724" // 图片的 key。仅在 tag 为 custom_icon 时生效。
235              },
236              "value": "selectDemo1" // 选项回调值,支持 string 类型数据。
237            }
238          ],
239          "confirm": {
240            // 二次确认弹窗配置
241            "title": {
242              "tag": "plain_text",
243              "content": "弹窗标题"
244            },
245            "text": {
246              "tag": "plain_text",
247              "content": "弹窗正文文案"
248            }
249          }
250        });
251
252        assert_eq!(serde_json::to_value(&select_static).unwrap(), json);
253    }
254}