open_lark/card/components/interactive_components/
image_picker.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 ImagePicker {
9    /// 组件的标签。多图选择的固定取值为 select_img。
10    tag: String,
11    /// 图片加载等状态时的组件风格样式。可取值:
12    ///
13    /// default:默认灰色样式
14    /// laser:彩色渐变样式,建议 AI 场景使用
15    #[serde(skip_serializing_if = "Option::is_none")]
16    style: Option<String>,
17    /// 图片是否多选。可选值:
18    ///
19    /// - true:多选,仅支持异步提交。多图选择组件需内嵌在表单容器中,否则卡片 JSON 报错。
20    /// - false:单选。
21    ///     - 组件在表单容器内时,图片选项展示为带单选按钮(radio button)的异步提交样式。
22    ///     - 组件不在表单容器内时,图片选项展示为不带单选按钮(radio button)的同步提交样式。
23    #[serde(skip_serializing_if = "Option::is_none")]
24    multi_select: Option<bool>,
25    /// 图片选项的布局方式。可选值:
26    ///
27    /// - stretch:每个选项的图片宽度撑满父容器宽度,高度按图片大小等比例缩放。
28    /// - bisect:二等分排布,每个选项图片宽度占父容器的 1/2,高度按图片大小等比例缩放。
29    /// - trisect:三等分排布,每个选项图片宽度占父容器的 1/3,高度按图片大小等比例缩放。
30    #[serde(skip_serializing_if = "Option::is_none")]
31    layout: Option<String>,
32    /// 自定义多图选择组件的名称作为唯一标识。用于识别用户提交的数据属于哪个组件。
33    ///
34    /// 注意:当多图选择组件嵌套在表单容器中时,该字段生效、必填,且需在卡片全局内唯一。
35    #[serde(skip_serializing_if = "Option::is_none")]
36    name: Option<String>,
37    /// 多图选择的选项是否必选。当组件内嵌在表单容器中时,该属性可用。其它情况将报错或不生效。
38    /// 可取值:
39    ///
40    /// - true:选项必填。当用户点击表单容器的“提交”时,未选择选项,则前端提示“有必填项未填写”,
41    ///   不会向开发者的服务端发起回传请求。
42    /// - false:选项选填。当用户点击表单容器的“提交”时,未选择选项,仍提交表单容器中的数据。
43    #[serde(skip_serializing_if = "Option::is_none")]
44    required: Option<bool>,
45    /// 点击图片选项后是否弹窗放大图片。当多图选择组件嵌套在表单容器中时,该属性生效。
46    ///
47    /// - true:点击图片后,弹出图片查看器放大查看当前点击的图片。
48    /// - false:点击图片后,响应卡片本身的交互事件,不弹出图片查看器。
49    #[serde(skip_serializing_if = "Option::is_none")]
50    can_preview: Option<bool>,
51    /// 选项中图片的宽高比。图片按最短边撑满图片渲染容器,按照居中裁剪的方式自适应裁剪。可取值:
52    ///
53    /// - 1:1
54    /// - 16:9
55    /// - 4:3
56    #[serde(skip_serializing_if = "Option::is_none")]
57    aspect_ratio: Option<String>,
58    /// 是否禁用整个选择组件。可选值:
59    ///
60    /// - true:禁用整个选择组件
61    /// - false:选择组件保持可用状态
62    #[serde(skip_serializing_if = "Option::is_none")]
63    disabled: Option<bool>,
64    /// 禁用整个组件后,用户将光标悬浮在整个组件上时展示的禁用提示文案。
65    #[serde(skip_serializing_if = "Option::is_none")]
66    disabled_tips: Option<PlainText>,
67    /// 你可在交互事件中自定义回传参数,支持回传字符串,或 "key":"value" 构成的对象结构体。
68    #[serde(skip_serializing_if = "Option::is_none")]
69    value: Option<Value>,
70    /// 选项值配置。按选项数组的顺序展示选项内容。
71    #[serde(skip_serializing_if = "Option::is_none")]
72    options: Option<Vec<SelectImageOption>>,
73}
74
75#[derive(Debug, Serialize, Deserialize, Default)]
76pub struct SelectImageOption {
77    /// 图片资源的 Key。你可以调用上传图片接口或在搭建工具中上传图片,获取图片的 key。
78    img_key: String,
79    /// 自定义每个图片选项的回传参数。在回传交互中指定的回传参数将透传至开发者的服务端。
80    #[serde(skip_serializing_if = "Option::is_none")]
81    value: Option<String>,
82    /// 是否禁用某个图片选项。可选值:
83    ///
84    /// - true:禁用该选项
85    /// - false:选项保持可用状态
86    #[serde(skip_serializing_if = "Option::is_none")]
87    disabled: Option<bool>,
88    /// 禁用某个图片选项后,用户将光标悬浮在选项上或点击选项时展示的禁用提示文案。
89    #[serde(skip_serializing_if = "Option::is_none")]
90    disabled_tips: Option<PlainText>,
91    /// 用户在 PC 端将光标悬浮在多图选择上方时的文案提醒。默认为空。
92    #[serde(skip_serializing_if = "Option::is_none")]
93    hover_tips: Option<PlainText>,
94}
95
96impl SelectImageOption {
97    pub fn new(img_key: &str) -> Self {
98        Self {
99            img_key: img_key.to_string(),
100            value: None,
101            disabled: None,
102            disabled_tips: None,
103            hover_tips: None,
104        }
105    }
106
107    pub fn value(mut self, value: &str) -> Self {
108        self.value = Some(value.to_string());
109        self
110    }
111
112    pub fn disabled(mut self, disabled: bool) -> Self {
113        self.disabled = Some(disabled);
114        self
115    }
116
117    pub fn disabled_tips(mut self, disabled_tips: PlainText) -> Self {
118        self.disabled_tips = Some(disabled_tips);
119        self
120    }
121
122    pub fn hover_tips(mut self, hover_tips: PlainText) -> Self {
123        self.hover_tips = Some(hover_tips);
124        self
125    }
126}
127
128impl Default for ImagePicker {
129    fn default() -> Self {
130        Self {
131            tag: "select_img".to_string(),
132            style: None,
133            multi_select: None,
134            layout: None,
135            name: None,
136            required: None,
137            can_preview: None,
138            aspect_ratio: None,
139            disabled: None,
140            disabled_tips: None,
141            value: None,
142            options: None,
143        }
144    }
145}
146
147impl ImagePicker {
148    pub fn new() -> Self {
149        Self::default()
150    }
151
152    pub fn style(mut self, style: &str) -> Self {
153        self.style = Some(style.to_string());
154        self
155    }
156
157    pub fn multi_select(mut self, multi_select: bool) -> Self {
158        self.multi_select = Some(multi_select);
159        self
160    }
161
162    pub fn layout(mut self, layout: &str) -> Self {
163        self.layout = Some(layout.to_string());
164        self
165    }
166
167    pub fn name(mut self, name: &str) -> Self {
168        self.name = Some(name.to_string());
169        self
170    }
171
172    pub fn required(mut self, required: bool) -> Self {
173        self.required = Some(required);
174        self
175    }
176
177    pub fn can_preview(mut self, can_preview: bool) -> Self {
178        self.can_preview = Some(can_preview);
179        self
180    }
181
182    pub fn aspect_ratio(mut self, aspect_ratio: &str) -> Self {
183        self.aspect_ratio = Some(aspect_ratio.to_string());
184        self
185    }
186
187    pub fn disabled(mut self, disabled: bool) -> Self {
188        self.disabled = Some(disabled);
189        self
190    }
191
192    pub fn disabled_tips(mut self, disabled_tips: PlainText) -> Self {
193        self.disabled_tips = Some(disabled_tips);
194        self
195    }
196
197    pub fn value(mut self, value: Value) -> Self {
198        self.value = Some(value);
199        self
200    }
201
202    pub fn options(mut self, options: Vec<SelectImageOption>) -> Self {
203        self.options = Some(options);
204        self
205    }
206}
207
208#[cfg(test)]
209mod test {
210    use serde_json::json;
211
212    use super::*;
213
214    #[test]
215    fn test_image_picker() {
216        let image_picker = ImagePicker::new()
217            .style("laser")
218            .multi_select(false)
219            .layout("bisect")
220            .name("choice_123")
221            .required(false)
222            .can_preview(false)
223            .aspect_ratio("16:9")
224            .disabled(false)
225            .disabled_tips(PlainText::text("用户禁用提示文案"))
226            .value(json!({"key": "value"}))
227            .options(vec![SelectImageOption::new("xxxxxxxxxxxxxx")
228                .value("picture1")
229                .disabled(false)
230                .disabled_tips(PlainText::text("用户禁用提示文案"))
231                .hover_tips(PlainText::text("第一张图"))]);
232
233        let json = json!(  {
234          "tag": "select_img", // 组件标签。
235          "style": "laser", // 选填,不填为默认样式。声明为 laser 时为镭射样式。
236          "multi_select": false, // 是否多选。
237          "layout": "bisect", // 选项的布局模式。
238          "name": "choice_123", // 自定义多图选择组件的名称作为唯一标识。当组件内嵌在表单容器中时,该字段生效且必填,用于识别用户提交的数据属于哪个组件。
239          "required": false, // 多图选择的选项是否必选。当组件内嵌在表单容器中时,该属性可用。其它情况将报错或不生效。
240          "can_preview": false, // 点击图片选项后是否弹窗放大图片。当多图选择组件嵌套在表单容器中时,该属性生效。
241          "aspect_ratio": "16:9", // 选项中图片的宽高比。
242          "disabled": false, // 是否禁用整个选择组件。
243          "disabled_tips": { // 指禁用组件后,用户将光标悬浮在整个组件上时展示的禁用提示文案。
244            "tag": "plain_text",
245            "content": "用户禁用提示文案"
246          },
247          "value": { // 自定义回传参数,支持回传字符串,或 "key":"value" 构成的对象结构体。
248            "key": "value"
249          },
250          // 选项数组。在此配置多图选择组件中每个图片选项的属性。
251          "options": [
252            {
253              "img_key": "xxxxxxxxxxxxxx", // 图片资源的 Key。
254              "value": "picture1", // 自定义每个图片选项的回传参数。
255              "disabled": false, // 是否禁用当前图片选项。
256              "disabled_tips": { // 禁用当前选项后,用户将光标悬浮在选项上或点击选项时展示的禁用提示文案。
257                "tag": "plain_text",
258                "content": "用户禁用提示文案"
259              },
260              "hover_tips": { // 用户在 PC 端将光标悬浮在选项上方时的文案提醒。
261                "tag": "plain_text",
262                "content": "第一张图"
263              },
264            }
265          ]
266        });
267
268        assert_eq!(json!(image_picker), json);
269    }
270}