open_lark/card/components/interactive_components/
input.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::card::components::content_components::plain_text::PlainText;
5
6/// 输入框组件
7#[derive(Debug, Serialize, Deserialize)]
8pub struct FeishuCardInput {
9    /// 输入框的标签。固定值为 input。
10    tag: String,
11    /// 输入框的唯一标识。当输入框内嵌在表单容器时,该属性生效,
12    /// 用于识别用户提交的文本属于哪个输入框。
13    ///
14    /// 注意:当输入框组件嵌套在表单容器中时,该字段必填且需在卡片全局内唯一。
15    #[serde(skip_serializing_if = "Option::is_none")]
16    name: Option<String>,
17    /// 输入框的内容是否必填。当输入框内嵌在表单容器时,该属性可用。其它情况将报错或不生效。
18    /// 可取值:
19    ///
20    /// - true:输入框必填。当用户点击表单容器的“提交”时,未填写输入框,
21    ///   则前端提示“有必填项未填写”,不会向开发者的服务端发起回传请求。
22    /// - false:输入框选填。当用户点击表单容器的“提交”时,未填写输入框,仍提交表单容器中的数据。
23    #[serde(skip_serializing_if = "Option::is_none")]
24    required: Option<bool>,
25    /// 是否禁用该输入框。该属性仅支持飞书 V7.4 及以上版本的客户端。可选值:
26    ///
27    /// true:禁用输入框组件
28    /// false:输入框组件保持可用状态
29    #[serde(skip_serializing_if = "Option::is_none")]
30    disabled: Option<bool>,
31    /// 输入框中的占位文本。
32    #[serde(skip_serializing_if = "Option::is_none")]
33    placeholder: Option<PlainText>,
34    /// 输入框中为用户预填写的内容。展示为用户在输入框中输入文本后待提交的样式。
35    #[serde(skip_serializing_if = "Option::is_none")]
36    default_value: Option<String>,
37    /// 输入框的宽度。支持以下枚举值:
38    ///
39    /// - default:默认宽度
40    /// - fill:卡片最大支持宽度
41    /// - [100,∞)px:自定义宽度。超出卡片宽度时将按最大支持宽度展示
42    #[serde(skip_serializing_if = "Option::is_none")]
43    width: Option<String>,
44    /// 输入框可容纳的最大文本长度,可取 1~1,000
45    /// 范围内的整数。当用户输入的文本字符数超过最大文本长度,组件将报错提示。
46    #[serde(skip_serializing_if = "Option::is_none")]
47    max_length: Option<u32>,
48    /// 文本标签,即对输入框的描述,用于提示用户要填写的内容。多用于表单容器中内嵌的输入框组件。
49    #[serde(skip_serializing_if = "Option::is_none")]
50    label: Option<PlainText>,
51    /// 文本标签的位置。可取值:
52    ///
53    /// - top:文本标签位于输入框上方
54    /// - left:文本标签位于输入框左边
55    ///   注意:在移动端等窄屏幕场景下,文本标签将自适应固定展示在输入框上方。
56    #[serde(skip_serializing_if = "Option::is_none")]
57    label_position: Option<String>,
58    /// 你可在交互事件中自定义回传数据,支持 string 或 object 数据类型。
59    #[serde(skip_serializing_if = "Option::is_none")]
60    value: Option<Value>,
61    /// 二次确认弹窗配置。指在用户提交时弹出二次确认弹窗提示;只有用户点击确认后,
62    /// 才提交输入的内容。该字段默认提供了确认和取消按钮,你只需要配置弹窗的标题与内容即可。
63    ///
64    /// 注意:confirm 字段仅在用户点击包含提交属性的按钮时才会触发二次确认弹窗。
65    #[serde(skip_serializing_if = "Option::is_none")]
66    confirm: Option<InputConfirm>,
67    /// 设置输入框组件的降级文案。由于输入框仅支持飞书 V6.8
68    /// 及以上版本的客户端,你需选择在低于此版本的客户端上,该组件的降级展示方式:
69    ///
70    /// 不填写该字段,使用系统默认的降级文案:“请升级至最新版本客户端,以查看内容”
71    /// "drop":填写 "drop",在旧版本客户端上直接丢弃该输入框组件
72    /// 使用 text 文本对象自定义降级文案
73    #[serde(skip_serializing_if = "Option::is_none")]
74    fallback: Option<InputFallback>,
75}
76
77impl Default for FeishuCardInput {
78    fn default() -> Self {
79        FeishuCardInput {
80            tag: "input".to_string(),
81            name: None,
82            required: None,
83            disabled: None,
84            placeholder: None,
85            default_value: None,
86            width: None,
87            max_length: None,
88            label: None,
89            label_position: None,
90            value: None,
91            confirm: None,
92            fallback: None,
93        }
94    }
95}
96
97#[derive(Debug, Serialize, Deserialize)]
98pub struct InputConfirm {
99    title: PlainText,
100    text: PlainText,
101}
102
103impl InputConfirm {
104    pub fn new(title: &str, text: &str) -> Self {
105        InputConfirm {
106            title: PlainText::text(title),
107            text: PlainText::text(text),
108        }
109    }
110
111    pub fn title(mut self, title: PlainText) -> Self {
112        self.title = title;
113        self
114    }
115
116    pub fn text(mut self, text: PlainText) -> Self {
117        self.text = text;
118        self
119    }
120}
121
122#[derive(Debug, Serialize, Deserialize)]
123pub struct InputFallback {
124    /// 降级文案的标签,固定取值为 fallback_text。
125    tag: String,
126    /// 降级文案的内容。
127    text: PlainText,
128}
129
130impl Default for InputFallback {
131    fn default() -> Self {
132        InputFallback {
133            tag: "fallback_text".to_string(),
134            text: PlainText::default(),
135        }
136    }
137}
138
139impl InputFallback {
140    pub fn new() -> Self {
141        Self::default()
142    }
143
144    pub fn text(mut self, text: PlainText) -> Self {
145        self.text = text;
146        self
147    }
148}
149
150impl FeishuCardInput {
151    pub fn new() -> Self {
152        FeishuCardInput::default()
153    }
154
155    pub fn name(mut self, name: &str) -> Self {
156        self.name = Some(name.to_string());
157        self
158    }
159
160    pub fn required(mut self, required: bool) -> Self {
161        self.required = Some(required);
162        self
163    }
164
165    pub fn disabled(mut self, disabled: bool) -> Self {
166        self.disabled = Some(disabled);
167        self
168    }
169
170    pub fn placeholder(mut self, placeholder: PlainText) -> Self {
171        self.placeholder = Some(placeholder);
172        self
173    }
174
175    pub fn default_value(mut self, default_value: &str) -> Self {
176        self.default_value = Some(default_value.to_string());
177        self
178    }
179
180    pub fn width(mut self, width: &str) -> Self {
181        self.width = Some(width.to_string());
182        self
183    }
184
185    pub fn max_length(mut self, max_length: u32) -> Self {
186        self.max_length = Some(max_length);
187        self
188    }
189
190    pub fn label(mut self, label: PlainText) -> Self {
191        self.label = Some(label);
192        self
193    }
194
195    pub fn label_position(mut self, label_position: &str) -> Self {
196        self.label_position = Some(label_position.to_string());
197        self
198    }
199
200    pub fn value(mut self, value: Value) -> Self {
201        self.value = Some(value);
202        self
203    }
204
205    pub fn confirm(mut self, confirm: InputConfirm) -> Self {
206        self.confirm = Some(confirm);
207        self
208    }
209
210    pub fn fallback(mut self, fallback: InputFallback) -> Self {
211        self.fallback = Some(fallback);
212        self
213    }
214
215    pub fn build(self) -> FeishuCardInput {
216        self
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use serde_json::json;
223
224    use crate::card::components::content_components::plain_text::PlainText;
225
226    use super::*;
227
228    #[test]
229    fn test_input_builder() {
230        let input = FeishuCardInput::new()
231            .name("input1")
232            .required(false)
233            .disabled(false)
234            .placeholder(PlainText::text("请输入"))
235            .default_value("demo")
236            .width("default")
237            .max_length(5)
238            .label(PlainText::text("请输入文本:"))
239            .label_position("left")
240            .value(json!({"k": "v"}))
241            .confirm(InputConfirm::new("title", "content"))
242            .fallback(InputFallback::new().text(PlainText::text("自定义声明")));
243
244        let json = json!({
245          "tag": "input", // 输入框的标签。
246          "name": "input1", // 输入框的唯一标识。当输入框内嵌在表单容器时,该属性生效,用于识别用户提交的文本属于哪个输入框。
247          "required": false, // 输入框的内容是否必填。当输入框内嵌在表单容器时,该属性可用。其它情况将报错或不生效。
248          "disabled": false, // 是否禁用该输入框组件。默认值 false。
249          "placeholder": {
250            // 输入框中的占位文本。
251            "tag": "plain_text",
252            "content": "请输入"
253          },
254          "default_value": "demo", // 输入框中为用户预填写的内容。
255          "width": "default", // 输入框的宽度。
256          "max_length": 5, // 输入框可容纳的最大文本长度。默认值 1000。
257          "label": {
258            // 文本标签,即对输入框的描述,用于提示用户要填写的内容。
259            "tag": "plain_text",
260            "content": "请输入文本:"
261          },
262          "label_position": "left", // 文本标签的位置。默认值 top。
263          "value": {
264            // 回传数据,支持 string 或 object 数据类型。
265            "k": "v"
266          },
267          "confirm": {
268            // 二次确认弹窗配置。
269            "title": {
270              "tag": "plain_text",
271              "content": "title"
272            },
273            "text": {
274              "tag": "plain_text",
275              "content": "content"
276            }
277          },
278          "fallback": {
279            // 设置输入框组件的降级文案。
280            "tag": "fallback_text", // 降级文案的标签。
281            "text": {
282              "content": "自定义声明", // 自定义降级文案的具体内容。
283              "tag": "plain_text" // 降级文案内容的标签。
284            }
285          }
286        });
287
288        assert_eq!(serde_json::to_value(input).unwrap(), json);
289    }
290}