open_lark/card/components/content_components/
table.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4#[derive(Debug, Serialize, Deserialize)]
5pub struct FeishuCardTable {
6 tag: String,
8 #[serde(skip_serializing_if = "Option::is_none")]
10 page_size: Option<i32>,
11 #[serde(skip_serializing_if = "Option::is_none")]
18 row_height: Option<String>,
19 #[serde(skip_serializing_if = "Option::is_none")]
21 header_style: Option<FeishuCardTableHeaderStyle>,
22 columns: Vec<FeishuCardTableColumn>,
24 rows: Value,
27}
28
29impl Default for FeishuCardTable {
30 fn default() -> Self {
31 FeishuCardTable {
32 tag: "table".to_string(),
33 page_size: None,
34 row_height: None,
35 header_style: None,
36 columns: vec![],
37 rows: Value::Null,
38 }
39 }
40}
41
42#[derive(Debug, Serialize, Deserialize, Default)]
44pub struct FeishuCardTableHeaderStyle {
45 #[serde(skip_serializing_if = "Option::is_none")]
51 text_align: Option<String>,
52 #[serde(skip_serializing_if = "Option::is_none")]
57 text_size: Option<String>,
58 #[serde(skip_serializing_if = "Option::is_none")]
63 background_style: Option<String>,
64 #[serde(skip_serializing_if = "Option::is_none")]
69 text_color: Option<String>,
70 #[serde(skip_serializing_if = "Option::is_none")]
75 bold: Option<bool>,
76 #[serde(skip_serializing_if = "Option::is_none")]
79 lines: Option<i32>,
80}
81
82impl FeishuCardTableHeaderStyle {
83 pub fn new() -> Self {
84 Self::default()
85 }
86
87 pub fn text_align(mut self, text_align: &str) -> Self {
88 self.text_align = Some(text_align.to_string());
89 self
90 }
91
92 pub fn text_size(mut self, text_size: &str) -> Self {
93 self.text_size = Some(text_size.to_string());
94 self
95 }
96
97 pub fn background_style(mut self, background_style: &str) -> Self {
98 self.background_style = Some(background_style.to_string());
99 self
100 }
101
102 pub fn text_color(mut self, text_color: &str) -> Self {
103 self.text_color = Some(text_color.to_string());
104 self
105 }
106
107 pub fn bold(mut self, bold: bool) -> Self {
108 self.bold = Some(bold);
109 self
110 }
111
112 pub fn lines(mut self, lines: i32) -> Self {
113 self.lines = Some(lines);
114 self
115 }
116}
117
118#[derive(Debug, Serialize, Deserialize)]
120pub struct FeishuCardTableColumn {
121 name: String,
123 #[serde(skip_serializing_if = "Option::is_none")]
125 display_name: Option<String>,
126 #[serde(skip_serializing_if = "Option::is_none")]
133 width: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
140 horizontal_align: Option<String>,
141 data_type: String,
148 #[serde(skip_serializing_if = "Option::is_none")]
151 format: Option<FeishuCardTableColumnFormat>,
152}
153
154#[derive(Debug, Serialize, Deserialize, Default)]
156pub struct FeishuCardTableColumnFormat {
157 #[serde(skip_serializing_if = "Option::is_none")]
160 precision: Option<i32>,
161 #[serde(skip_serializing_if = "Option::is_none")]
163 symbol: Option<String>,
164 #[serde(skip_serializing_if = "Option::is_none")]
166 separator: Option<bool>,
167}
168
169impl FeishuCardTableColumnFormat {
170 pub fn new() -> Self {
171 FeishuCardTableColumnFormat::default()
172 }
173
174 pub fn precision(mut self, precision: i32) -> Self {
175 self.precision = Some(precision);
176 self
177 }
178
179 pub fn symbol(mut self, symbol: &str) -> Self {
180 self.symbol = Some(symbol.to_string());
181 self
182 }
183
184 pub fn separator(mut self, separator: bool) -> Self {
185 self.separator = Some(separator);
186 self
187 }
188}
189
190impl Default for FeishuCardTableColumn {
191 fn default() -> Self {
192 FeishuCardTableColumn {
193 name: "".to_string(),
194 display_name: None,
195 width: None,
196 horizontal_align: None,
197 data_type: "".to_string(),
198 format: None,
199 }
200 }
201}
202
203impl FeishuCardTableColumn {
204 pub fn new() -> Self {
205 Self::default()
206 }
207
208 pub fn name(mut self, name: &str) -> Self {
209 self.name = name.to_string();
210 self
211 }
212
213 pub fn display_name(mut self, display_name: &str) -> Self {
214 self.display_name = Some(display_name.to_string());
215 self
216 }
217
218 pub fn width(mut self, width: &str) -> Self {
219 self.width = Some(width.to_string());
220 self
221 }
222
223 pub fn horizontal_align(mut self, horizontal_align: &str) -> Self {
224 self.horizontal_align = Some(horizontal_align.to_string());
225 self
226 }
227
228 pub fn data_type(mut self, data_type: &str) -> Self {
229 self.data_type = data_type.to_string();
230 self
231 }
232
233 pub fn format(mut self, format: FeishuCardTableColumnFormat) -> Self {
234 self.format = Some(format);
235 self
236 }
237}
238
239impl FeishuCardTable {
240 pub fn new() -> Self {
241 Self::default()
242 }
243
244 pub fn page_zie(mut self, page_zie: i32) -> Self {
245 self.page_size = Some(page_zie);
246 self
247 }
248
249 pub fn row_height(mut self, row_height: &str) -> Self {
250 self.row_height = Some(row_height.to_string());
251 self
252 }
253
254 pub fn header_style(mut self, header_style: FeishuCardTableHeaderStyle) -> Self {
255 self.header_style = Some(header_style);
256 self
257 }
258
259 pub fn columns(mut self, columns: Vec<FeishuCardTableColumn>) -> Self {
260 self.columns = columns;
261 self
262 }
263
264 pub fn rows(mut self, rows: Value) -> Self {
265 self.rows = rows;
266 self
267 }
268}
269
270#[cfg(test)]
271mod test {
272 use serde_json::json;
273
274 use crate::card::components::content_components::table::{
275 FeishuCardTable, FeishuCardTableColumn, FeishuCardTableColumnFormat,
276 FeishuCardTableHeaderStyle,
277 };
278
279 #[test]
280 fn test_table() {
281 let table = FeishuCardTable::new()
282 .page_zie(1)
283 .row_height("middle")
284 .header_style(
285 FeishuCardTableHeaderStyle::new()
286 .bold(true)
287 .background_style("grey")
288 .lines(1)
289 .text_size("heading")
290 .text_align("center")
291 ,
292 )
293 .columns(vec![
294 FeishuCardTableColumn::new()
295 .name("customer_name")
296 .display_name("客户名称")
297 .data_type("text"),
298 FeishuCardTableColumn::new()
299 .name("customer_scale")
300 .display_name("客户规模")
301 .data_type("options")
302 .width("90px"),
303 FeishuCardTableColumn::new()
304 .name("customer_arr")
305 .display_name("ARR(万元)")
306 .data_type("number")
307 .format(
308 FeishuCardTableColumnFormat::new()
309 .precision(2)
310 .symbol("¥"),
311 )
312 .width("120px"),
313 FeishuCardTableColumn::new()
314 .name("customer_year")
315 .display_name("签约年限")
316 .data_type("text"),
317 ])
318 .rows(json!([
319 {
320 "customer_name": "飞书消息卡片是飞书中的一种功能,它允许用户通过机器人或应用以结构化(JSON)的方式发送和接收消息。",
321 "customer_scale": [
322 {
323 "text": "S2",
324 "color": "green"
325 }
326 ],
327 "customer_arr": 26.57774928467545,
328 "customer_year": "2年"
329 }
330 ]));
331
332 let json = json!( {
333 "tag": "table",
334 "page_size": 1,
335 "row_height": "middle",
336 "header_style": {
337 "bold": true,
338 "background_style": "grey",
339 "lines": 1,
340 "text_size": "heading",
341 "text_align": "center"
342 },
343 "columns": [
344 {
345 "name": "customer_name",
346 "display_name": "客户名称",
347 "data_type": "text"
348 },
349 {
350 "name": "customer_scale",
351 "display_name": "客户规模",
352 "data_type": "options",
353 "width": "90px"
354 },
355 {
356 "name": "customer_arr",
357 "display_name": "ARR(万元)",
358 "data_type": "number",
359 "format": {
360 "symbol": "¥",
361 "precision": 2
362 },
363 "width": "120px"
364 },
365 {
366 "name": "customer_year",
367 "display_name": "签约年限",
368 "data_type": "text"
369 }
370 ],
371 "rows": [
372 {
373 "customer_name": "飞书消息卡片是飞书中的一种功能,它允许用户通过机器人或应用以结构化(JSON)的方式发送和接收消息。",
374 "customer_scale": [
375 {
376 "text": "S2",
377 "color": "green"
378 }
379 ],
380 "customer_arr": 26.57774928467545,
381 "customer_year": "2年"
382 }
383 ]
384 });
385
386 assert_eq!(serde_json::to_value(&table).unwrap(), json);
387 }
388}