1use crate::request::{ContentType, Method, Request};
2use crate::{CONFIG, GLOBAL_DATA, PLUGIN_TOOLS};
3use crate::{ApiResponse, Tools};
4use br_fields::Field;
5use json::{array, object, JsonValue};
6use std::any::type_name;
7use std::collections::HashMap;
8use crate::tables::Tables;
9
10pub trait Action {
12 fn _name(&self) -> String {
14 type_name::<Self>().rsplit("::").next().unwrap().to_lowercase()
15 }
16 fn module_name(&self) -> String {
18 let t = type_name::<Self>().split("::").collect::<Vec<&str>>();
19 let plugin = t[2].to_lowercase();
20 let module = t[3].to_lowercase();
21 format!("{plugin}.{module}")
22 }
23 fn addon_name(&self) -> String {
25 let t = type_name::<Self>().split("::").collect::<Vec<&str>>();
26 t[2].to_lowercase()
27 }
28 fn api(&self) -> String {
30 let t = type_name::<Self>().split("::").collect::<Vec<&str>>();
31 let plugin = t[2].to_lowercase();
32 let module = t[3].to_lowercase();
33 let action = t[4].to_lowercase();
34 format!("{plugin}.{module}.{action}")
35 }
36 fn token(&self) -> bool {
38 true
39 }
40
41 fn sort(&self) -> usize {
42 99
43 }
44 fn title(&self) -> &'static str;
46 fn description(&self) -> &'static str {
48 ""
49 }
50 fn path(&self) -> &'static str {
52 ""
53 }
54 fn query(&self) -> JsonValue {
56 object! {}
57 }
58 fn tags(&self) -> &'static [&'static str] {
60 &[]
61 }
62 fn icon(&self) -> &'static str {
63 ""
64 }
65 fn public(&self) -> bool {
67 true
68 }
69 fn auth(&self) -> bool {
71 true
72 }
73 fn interface_type(&self) -> InterfaceType {
75 InterfaceType::API
76 }
77 fn method(&mut self) -> Method {
79 Method::Post
80 }
81 fn content_type(&mut self) -> ContentType {
83 ContentType::Json
84 }
85 fn params_check(&mut self) -> bool {
87 true
88 }
89 fn params(&mut self) -> JsonValue {
91 object! {}
92 }
93 fn success(&mut self) -> ApiResponse {
95 let mut data = object! {};
96 data["code"] = br_fields::int::Int::new(true, "code", "编号", 10, 0).example(0.into()).swagger();
97 data["message"] = br_fields::str::Str::new(true, "message", "成功消息", 256, "").example("成功".into()).swagger();
98 data["data"] = br_fields::text::Json::new(true, "data", "返回数据", object! {}).swagger();
99 data["success"] = br_fields::int::Switch::new(true, "success", "成功状态", true).example(true.into()).swagger();
100 data["timestamp"] = br_fields::datetime::Timestamp::new(true, "timestamp", "时间戳", 0, 0.0).swagger();
101 ApiResponse::success(data, "请求成功")
102 }
103 fn error(&mut self) -> ApiResponse {
105 let mut data = object! {};
106 data["code"] = br_fields::int::Int::new(true, "code", "编号", 10, 1000).example(1000.into()).swagger();
107 data["message"] = br_fields::str::Str::new(true, "message", "错误消息", 256, "").example("失败".into()).swagger();
108 data["data"] = br_fields::text::Json::new(true, "data", "返回数据", object! {}).swagger();
109 data["success"] = br_fields::int::Switch::new(true, "success", "成功状态", false).example(false.into()).swagger();
110 data["timestamp"] = br_fields::datetime::Timestamp::new(true, "timestamp", "时间戳", 0, 0.0).swagger();
111 ApiResponse::error(data, "请求失败")
112 }
113 fn run(&mut self, mut request: Request) -> Result<ApiResponse, ApiResponse> {
115 if self.public() && !self.method().str().is_empty() && self.method().str().to_lowercase() != request.method.str().to_lowercase() {
116 return Err(ApiResponse::fail(
117 -1,
118 format!(
119 "Request type error: Actual [{}] Expected [{}]",
120 request.method.str(),
121 self.method().str()
122 ).as_str(),
123 ));
124 }
125 let params = self.params().clone();
126 if self.params_check() {
127 self.check(&mut request.query.clone(), self.query().clone())?;
128 self.check(&mut request.body, params)?;
129 }
130 let res = self.index(request.clone());
131 match res.success {
132 true => Ok(res),
133 false => Err(res),
134 }
135 }
136 fn check(&mut self, request: &mut JsonValue, params: JsonValue) -> Result<(), ApiResponse> {
138 let req = request.clone();
139 for (name, _) in req.entries() {
140 if !params.has_key(name) {
141 request.remove(name);
142 }
143 }
144 for (name, field) in params.entries() {
145 let require = field["require"].as_bool().unwrap_or(false);
146 let title = field["title"].as_str().unwrap_or("");
147 if request.has_key(name) {
148 match field["mode"].as_str().unwrap() {
150 "key" => {
151 if !request[name].is_string() {
152 return Err(ApiResponse::fail(
153 900_001,
154 format!(
155 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
156 name, field["mode"]
157 ).as_str(),
158 ));
159 }
160 if require && request[name].is_empty() {
161 return Err(ApiResponse::fail(
162 900014,
163 format!("请求参数数据类型错误: 参数 [{name}] 不能为空").as_str(),
164 ));
165 }
166 }
167 "text" | "table" | "tree" => {
168 if !request[name].is_string() {
169 return Err(ApiResponse::fail(
170 900002,
171 format!(
172 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
173 name, field["mode"]
174 ).as_str(),
175 ));
176 }
177 if require && request[name].is_empty() {
178 return Err(ApiResponse::fail(
179 900002,
180 format!("{title} 必填").as_str(),
181 ));
182 }
183 }
184 "file" => {
185 if !request[name].is_array() && !request[name].is_string() {
186 return Err(ApiResponse::fail(
187 900003,
188 format!("参数 [{}] 数据类型应为[{}]", name, field["mode"]).as_str(),
189 ));
190 }
191 if require && request[name].is_empty() {
192 return Err(ApiResponse::fail(
193 900002,
194 format!("{title} 必填").as_str(),
195 ));
196 }
197 }
198 "int" => {
199 if require && request[name].to_string().is_empty() {
200 return Err(ApiResponse::fail(
201 900_002,
202 format!("{title} 必填").as_str(),
203 ));
204 }
205 if !request[name].to_string().is_empty() {
206 match request[name].to_string().parse::<i64>() {
207 Ok(_) => {}
208 Err(_) => {
209 return Err(ApiResponse::fail(
210 900013,
211 format!(
212 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
213 name, field["mode"]
214 ).as_str(),
215 ));
216 }
217 }
218 }
219 }
220 "timestamp" => {
221 if require && request[name].to_string().is_empty() {
222 return Err(ApiResponse::fail(
223 900002,
224 format!("{title} 必填").as_str(),
225 ));
226 }
227 if !request[name].to_string().is_empty() {
228 if request[name].is_array() && request[name].len() == 2 {
229 for item in request[name].members() {
231 match item.to_string().parse::<f64>() {
232 Ok(_) => {}
233 Err(_) => {
234 return Err(ApiResponse::fail(
235 900_013,
236 format!(
237 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
238 name, field["mode"]
239 ).as_str(),
240 ));
241 }
242 }
243 }
244 } else {
245 match request[name].to_string().parse::<f64>() {
246 Ok(_) => {}
247 Err(_) => {
248 return Err(ApiResponse::fail(
249 900013,
250 format!(
251 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
252 name, field["mode"]
253 ).as_str(),
254 ));
255 }
256 }
257 }
258 }
259 }
260 "float" => {
261 if require && request[name].to_string().is_empty() {
262 return Err(ApiResponse::fail(
263 900002,
264 format!("{title} 必填").as_str(),
265 ));
266 }
267 if !request[name].to_string().is_empty() {
268 match request[name].to_string().parse::<f64>() {
269 Ok(_) => {}
270 Err(_) => {
271 return Err(ApiResponse::fail(
272 900023,
273 format!(
274 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
275 name, field["mode"]
276 ).as_str(),
277 ));
278 }
279 }
280 }
281 }
282 "string" | "time" | "code" | "pass" | "email" | "location" | "color" | "date" | "barcode" | "datetime" | "editor" | "tel" => {
283 if !request[name].is_string() {
284 return Err(ApiResponse::fail(
285 -1,
286 format!(
287 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
288 name, field["mode"]
289 ).as_str(),
290 ));
291 }
292 if require && request[name].is_empty() {
293 return Err(ApiResponse::fail(
294 900004,
295 format!("{title} 必填").as_str(),
296 ));
297 }
298 }
299 "dict" => {
300 if require && request[name].is_empty() {
301 return Err(ApiResponse::fail(
302 900005,
303 format!("{title} 必填").as_str(),
304 ));
305 }
306 }
307 "switch" => {
308 match request[name].to_string().parse::<bool>() {
309 Ok(e) => {
310 request[name] = e.into();
311 }
312 Err(_) => {
313 return Err(ApiResponse::fail(
314 -1,
315 format!("请求参数数据类型错误: 参数 [{name}] 数据类型应为[{}]", field["mode"]).as_str(),
316 ));
317 }
318 }
319 }
320 "select" => {
321 if !request[name].is_array() && !request[name].is_string() {
322 return Err(ApiResponse::fail(
323 -1,
324 format!(
325 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
326 name, field["mode"]
327 ).as_str(),
328 ));
329 }
330 let value = if request[name].is_array() {
331 request[name].members().map(|m| m.to_string()).collect::<Vec<String>>()
332 } else {
333 request[name].to_string().split(",").map(|x| x.to_string()).collect::<Vec<String>>()
334 };
335 let option = field["option"].members().map(|m| m.as_str().unwrap_or("")).collect::<Vec<&str>>();
336
337 let require = field["require"].as_bool().unwrap();
338 for item in value.clone() {
339 if !option.contains(&&*item.clone()) && (item.is_empty() && require) {
340 let option = field["option"].members().map(|m| m.as_str().unwrap_or("")).collect::<Vec<&str>>().join(",");
341 return Err(ApiResponse::fail(-1, format!("请求参数选项错误: 参数 [{item}] 数据类型应为[{option}]之内").as_str()));
342 }
343 }
344 request[name] = value.into();
345 }
346 "radio" => {
347 if !request[name].is_string() {
348 return Err(ApiResponse::fail(
349 -1,
350 format!(
351 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}] 实际为[{}]",
352 name, field["mode"], request[name]
353 ).as_str(),
354 ));
355 }
356 let option = field["option"].members().map(|m| m.as_str().unwrap_or("")).collect::<Vec<&str>>().join(",");
357 if request[name].is_string() && !field["option"].contains(request[name].as_str().unwrap_or(""))
358 {
359 return Err(ApiResponse::fail(
360 -1,
361 format!(
362 "请求参数选项错误: 参数 [{}] 数据 [{}] 应为 [{}] 之一",
363 name, request[name], option
364 ).as_str(),
365 ));
366 }
367 }
368 "array" | "polygon" => {
369 if !request[name].is_array() {
370 return Err(ApiResponse::fail(
371 900_009,
372 format!(
373 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
374 name, field["mode"]
375 ).as_str(),
376 ));
377 }
378 if require && request[name].is_empty() {
379 return Err(ApiResponse::fail(
380 900010,
381 format!("请求参数数据类型错误: 参数 [{name}] 不能为空").as_str(),
382 ));
383 }
384 }
385 "object" => {
386 if !request[name].is_object() {
387 return Err(ApiResponse::fail(
388 900009,
389 format!(
390 "请求参数数据类型错误: 参数 [{}] 数据类型应为[{}]",
391 name, field["mode"]
392 ).as_str(),
393 ));
394 }
395 if require && request[name].is_empty() {
396 return Err(ApiResponse::fail(
397 900006,
398 format!("{title} 必填").as_str(),
399 ));
400 }
401 }
402 _ => {
403 println!("检查未知类型: {}", field["mode"])
404 }
405 }
406 } else {
407 if require {
408 return Err(ApiResponse::fail(
409 900007,
410 format!("{title} 必填").as_str(),
411 ));
412 }
413 request[name] = field["def"].clone();
414 }
415 }
416 Ok(())
417 }
418 fn index(&mut self, request: Request) -> ApiResponse;
420 #[cfg(any(feature = "sqlite", feature = "mssql", feature = "mysql", feature = "pgsql"))]
421 fn table_select(
422 &mut self,
423 request: JsonValue,
424 table_name: &str,
425 fields: Vec<&str>,
426 ) -> JsonValue {
427 self.table().main_select_fields(table_name, fields).params(request.clone()).get_table_select()
428 }
429 fn params_table_tree_select(&mut self, mut params: JsonValue) -> JsonValue {
431 params["ids"] = br_fields::text::Array::new(false, "ids", "加载已选中数据", array![]).field();
432 params["search"] = br_fields::str::Str::new(false, "search", "搜索", 200, "").example("".into()).field();
433 params["pid"] = br_fields::str::Str::new(false, "pid", "上级ID", 20, "").example("".into()).field();
434 params["where_or"] = br_fields::text::Array::new(false, "where_or", "查询条件", array![]).example(array![]).field();
435 params["where_and"] = br_fields::text::Array::new(false, "where_and", "查询条件", array![]).example(array![]).field();
436 params["params"] = br_fields::text::Object::new(false, "params", "关联数据参数", object! {}).example(object! {}).field();
437 params
438 }
439 #[cfg(any(feature = "sqlite", feature = "mssql", feature = "mysql", feature = "pgsql"))]
440 fn table_tree_select(
441 &mut self,
442 request: JsonValue,
443 table_name: &str,
444 pid_field: &str,
445 label_field: &str,
446 fields: Vec<&str>,
447 ) -> JsonValue {
448 let ids = request["ids"].clone();
449 let search = request["search"].as_str().unwrap_or("");
450 let pid_id = request["pid"].as_str().unwrap_or("id");
451
452 let where_or = request["where_or"].clone();
453 let where_and = request["where_and"].clone();
454
455 let mut tools = self.tools();
456 let db = tools.db.table(table_name);
457
458 db.where_and(pid_field, "=", pid_id.into());
459
460 if !search.is_empty() {
461 db.where_and("id", "in", search.into());
462 }
463
464 for (key, value) in where_or.entries() {
465 if value.is_empty() {
466 continue;
467 }
468 if value.is_array() {
469 db.where_or(key, "between", value.clone());
470 db.where_or(key, "in", value.clone());
471 } else if value.is_boolean() {
472 db.where_or(key, "=", value.clone());
473 } else {
474 db.where_or(key, "like", format!("%{value}%").into());
475 }
476 }
477
478 for value in where_and.members() {
479 db.where_and(
480 value[0].as_str().unwrap(),
481 value[1].as_str().unwrap(),
482 value[2].clone(),
483 );
484 }
485
486 db.field(format!("id,{},{}", pid_field, fields.join(",")).as_str());
487
488 let mut data = db.select();
489 for item in data.members_mut() {
490 let children = self.tools().db.table(table_name).where_and(pid_field, "=", item["id"].clone()).select();
491 if children.is_empty() {
492 item["lazy"] = false.into();
493 } else {
494 item["lazy"] = true.into();
495 }
496 }
497
498 let ids_list = if ids.is_empty() {
499 vec![]
500 } else {
501 let list = self.tools().db.table(table_name).where_and("id", "in", ids).field(format!("id,{},{}", pid_field, fields.join(",")).as_str()).select();
502 let mut t = vec![];
503 for item in list.members() {
504 let mut v = vec![];
505 let mut pid = item[pid_field].to_string();
506 v.push(item[label_field].to_string());
507 loop {
508 if pid.is_empty() {
509 break;
510 }
511 let find = self.tools().db.table(table_name).where_and("id", "=", pid.into()).field(format!("id,{},{}", pid_field, fields.join(",")).as_str()).find();
512 pid = find[pid_field].to_string();
513 v.push(find[label_field].to_string());
514 }
515 v.reverse();
516 t.push(object! {
517 id:item["id"].clone(),
518 label:v.join("/"),
519 });
520 }
521 t
522 };
523
524
525 object! {
526 data:data,
527 label_field:label_field,
528 pid_field:pid_field,
529 ids:ids_list.clone()
530
531 }
532 }
533 fn params_table_menu(&mut self, mut params: JsonValue) -> JsonValue {
535 params["search"] = br_fields::str::Str::new(false, "search", "搜索", 200, "").example("".into()).field();
536 params["where_or"] = br_fields::text::Array::new(false, "where_or", "查询条件", array![]).example(array![]).field();
537 params["where_and"] = br_fields::text::Array::new(false, "where_and", "查询条件", array![]).example(array![]).field();
538 params
539 }
540 #[allow(clippy::too_many_arguments)]
541 #[cfg(any(feature = "sqlite", feature = "mssql", feature = "mysql", feature = "pgsql"))]
542 fn table_menu(
543 &mut self,
544 request: JsonValue,
545 table_name: &str,
546 label_field: &str,
547 fields: JsonValue,
548
549 hidd_field: Vec<&str>,
550 show_field: Vec<&str>,
551 search_fields: Vec<&str>,
552 ) -> JsonValue {
553 let search = request["search"].as_str().unwrap_or("");
554
555 let order = request["order"].clone();
556 let where_or = request["where_or"].clone();
557 let where_and = request["where_and"].clone();
558 let mut columns = array![];
559 let mut search_name = vec![];
560 let mut search_field = vec![];
561 let mut filter_columns = vec![];
562 let mut table_fields = object! {};
563 let mut table_field_json = vec![];
564
565 let mut alls_field = fields.entries().map(|(x, _)| x).collect::<Vec<&str>>();
566 alls_field.retain(|x| !hidd_field.contains(x));
567 if !show_field.is_empty() {
568 alls_field.retain(|x| show_field.contains(x) || *x == "id");
569 }
570
571 for item in alls_field.iter() {
572 let row = &fields[item.to_string()];
573 let field = row["field"].to_string();
574 let title = row["title"].to_string();
575 let mode = row["mode"].to_string();
576
577 if search_fields.contains(&field.as_str()) {
578 search_name.push(title);
579 search_field.push(field.clone());
580 }
581 filter_columns.push(self.filter_column(row.clone()));
582 columns.push(self.table_column(row.clone())).unwrap();
583 let mut table_field_info = object! {};
584 match mode.as_str() {
585 "table" => table_field_info = row.clone(),
586 "array" => table_field_json.push(field.clone()),
587 _ => {}
588 }
589 if !table_field_info.is_empty() {
590 table_fields[field.as_str()] = table_field_info;
591 }
592 }
593
594 let mut tools = self.tools();
595 let db = tools.db.table(table_name);
596
597 if !search.is_empty() {
598 db.where_and(
599 &search_field.join("|"),
600 "like",
601 format!("%{search}%").into(),
602 );
603 }
604
605 for (key, value) in where_or.entries() {
606 if value.is_empty() {
607 continue;
608 }
609 if value.is_array() {
610 db.where_or(key, "between", value.clone());
611 db.where_or(key, "in", value.clone());
612 } else if value.is_boolean() {
613 db.where_or(key, "=", value.clone());
614 } else {
615 db.where_or(key, "like", format!("%{value}%").into());
616 }
617 }
618
619 for value in where_and.members() {
620 db.where_and(
621 value[0].as_str().unwrap(),
622 value[1].as_str().unwrap(),
623 value[2].clone(),
624 );
625 }
626
627 let mut db_list = db.clone();
628
629 for item in order.members() {
630 db_list.order(item[0].as_str().unwrap(), item[1].as_bool().unwrap());
631 }
632
633 let t = alls_field.join(",");
634 db_list.field(t.as_str());
635
636 if !table_field_json.is_empty() {
637 db_list.json(&table_field_json.join(","));
638 }
639
640 let mut data = db_list.select();
641
642 let mut table_datas = object! {};
643 let mut ids = HashMap::new();
644
645 for item in data.members_mut() {
646 for (field, _) in table_fields.entries() {
647 if table_datas[field].is_empty() {
648 table_datas[field] = array![];
649 }
650 let _ = table_datas[field].push(item[field].clone());
651 }
652 }
653 for (field, info) in table_fields.entries_mut() {
654 let _ = info["fields"].push("id");
655 let fields_k = info["fields"].members().map(|x| x.as_str().unwrap()).collect::<Vec<&str>>();
656 let table_name = info["table"].clone();
657 let mut data_list = self.tools().db.table(table_name.as_str().unwrap()).where_and("id", "in", table_datas[field].clone()).field(&fields_k.join(",")).select();
658
659 for item in data_list.members_mut() {
660 let id = item["id"].as_str().unwrap_or("").to_string();
661 item.remove("id");
662 let label = item.entries().map(|(_, v)| v.as_str().unwrap()).collect::<Vec<&str>>();
663 ids.insert(
664 id.clone(),
665 object! {
666 value: id.clone(),
667 label:label.join(" | ").clone(),
668 },
669 );
670 }
671 }
672
673 for item in data.members_mut() {
674 for (field, _) in table_fields.entries() {
675 if item[field].is_empty() {
676 continue;
677 }
678 item[field] = ids[item[field].as_str().unwrap()].clone();
679 }
680 }
681
682 object! {
683 data:data,
684 columns:columns,
685 search_name:search_name.join("/"),
686 label_field:label_field,
687 btn_all:array![],
688 btn_api:array![],
689 btn_ids:array![],
690 }
691 }
692 #[allow(clippy::too_many_arguments)]
693 #[cfg(any(feature = "sqlite", feature = "mssql", feature = "mysql", feature = "pgsql"))]
694 fn table_tree(
695 &mut self,
696 request: JsonValue,
697 table_name: &str,
698 pid_field: &str,
699 label_field: &str,
700 fields: JsonValue,
701 hidd_field: Vec<&str>,
702 show_field: Vec<&str>,
703 search_fields: Vec<&str>,
704 _filter_fields: Vec<&str>,
705 ) -> JsonValue {
706 let search = request["search"].as_str().unwrap_or("");
707
708 let pid = request["pid"].clone();
709
710 let order = request["order"].clone();
711 let where_or = request["where_or"].clone();
712 let where_and = request["where_and"].clone();
713 let mut columns = array![];
714 let mut search_name = vec![];
715 let mut search_field = vec![];
716 let mut filter_columns = vec![];
717 let mut table_field_json = vec![];
718 let mut table_fields = object! {};
719
720 let mut alls_field = fields.entries().map(|(x, _)| x).collect::<Vec<&str>>();
721 alls_field.retain(|x| !hidd_field.contains(x));
722 if !show_field.is_empty() {
723 alls_field.retain(|x| show_field.contains(x) || *x == "id");
724 }
725
726 for item in alls_field.iter() {
727 let row = fields[item.to_string()].clone();
728 let field = row["field"].to_string();
729 let title = row["title"].to_string();
730 let mode = row["mode"].to_string();
731
732 if search_fields.contains(&field.as_str()) {
733 search_name.push(title);
734 search_field.push(field.clone());
735 }
736 filter_columns.push(self.filter_column(row.clone()));
737 columns.push(self.table_column(row.clone())).unwrap();
738 let mut table_field_info = object! {};
739 match mode.as_str() {
740 "table" => table_field_info = row.clone(),
741 "array" => table_field_json.push(field.clone()),
742 _ => {}
743 }
744 if !table_field_info.is_empty() {
745 table_fields[field.as_str()] = table_field_info;
746 }
747 }
748
749 let mut tools = self.tools();
750 let db = tools.db.table(table_name);
751 db.where_and(pid_field, "=", pid);
752 if !search.is_empty() {
753 db.where_and(
754 &search_field.join("|"),
755 "like",
756 format!("%{search}%").into(),
757 );
758 }
759
760 for (key, value) in where_or.entries() {
761 if value.is_empty() {
762 continue;
763 }
764 if value.is_array() {
765 db.where_or(key, "in", value.clone());
767 } else if value.is_boolean() {
768 db.where_or(key, "=", value.clone());
769 } else {
770 db.where_or(key, "like", format!("%{value}%").into());
771 }
772 }
773
774 for value in where_and.members() {
775 db.where_and(
776 value[0].as_str().unwrap(),
777 value[1].as_str().unwrap(),
778 value[2].clone(),
779 );
780 }
781
782 let mut db_list = db.clone();
783
784 for item in order.members() {
785 db_list.order(item[0].as_str().unwrap(), item[1].as_bool().unwrap());
786 }
787 let t = alls_field.join(",");
788 db_list.field(t.as_str());
789 if !table_field_json.is_empty() {
790 db_list.json(&table_field_json.join(","));
791 }
792 let mut data = db_list.select();
793
794 let mut table_datas = object! {};
795 let mut ids = HashMap::new();
796
797 for item in data.members_mut() {
798 for (field, _) in table_fields.entries() {
799 if table_datas[field].is_empty() {
800 table_datas[field] = array![];
801 }
802 let _ = table_datas[field].push(item[field].clone());
803 }
804 }
805 for (field, info) in table_fields.entries_mut() {
806 let _ = info["fields"].push("id");
807 let fields_k = info["fields"].members().map(|x| x.as_str().unwrap()).collect::<Vec<&str>>();
808 let table_name = info["table"].clone();
809 let mut data_list = self.tools().db.table(table_name.as_str().unwrap()).where_and("id", "in", table_datas[field].clone()).field(&fields_k.join(",")).select();
810
811 for item in data_list.members_mut() {
812 let id = item["id"].as_str().unwrap_or("").to_string();
813 item.remove("id");
814 let label = item.entries().map(|(_, v)| v.as_str().unwrap()).collect::<Vec<&str>>();
815 ids.insert(
816 id.clone(),
817 object! {
818 value: id.clone(),
819 label:label.join(" | ").clone(),
820 },
821 );
822 }
823 }
824
825 let mut list = array![];
826 for item in data.members_mut() {
827 for (field, _) in table_fields.entries() {
828 if item[field].is_empty() {
829 continue;
830 }
831 item[field] = ids[item[field].as_str().unwrap()].clone();
832 }
833 let _ = list.push(object! {
834 id:item["id"].as_str().unwrap(),
835 code:item["code"].as_str().unwrap(),
836 label:item[label_field].as_str().unwrap(),
837 data:item.clone(),
838 });
839 }
840
841 object! {
842 data:list,
843 columns:columns,
844 search_name:search_name.join("/"),
845 filter_columns:filter_columns,
846 pid_field:pid_field,
847 btn_all:array![],
848 btn_api:array![]
849 }
850 }
851
852 #[allow(clippy::too_many_arguments)]
853 #[cfg(any(feature = "sqlite", feature = "mssql", feature = "mysql", feature = "pgsql"))]
854 fn tree(&mut self, request: JsonValue, table_name: &str, pid_field: &str, label: &str, fields: JsonValue, hidd_field: Vec<&str>, show_field: Vec<&str>, search_fields: Vec<&str>, _filter_fields: Vec<&str>) -> JsonValue {
856 let search = request["search"].as_str().unwrap_or("");
857
858 let pid = request["pid"].clone();
859 let order = request["order"].clone();
860 let where_or = request["where_or"].clone();
861 let where_and = request["where_and"].clone();
862 let mut columns = array![];
863 let mut search_name = vec![];
864 let mut search_field = vec![];
865 let mut filter_columns = vec![];
866 let mut table_field_json = vec![];
867 let mut table_fields = object! {};
868
869 let mut alls_field = fields.entries().map(|(x, _)| x).collect::<Vec<&str>>();
870 alls_field.retain(|x| !hidd_field.contains(x));
871 if !show_field.is_empty() {
872 alls_field.retain(|x| show_field.contains(x) || *x == "id");
873 }
874
875 for item in alls_field.iter() {
876 let row = fields[item.to_string()].clone();
877 let field = row["field"].to_string();
878 let title = row["title"].to_string();
879 let mode = row["mode"].to_string();
880
881 if search_fields.contains(&field.as_str()) {
882 search_name.push(title);
883 search_field.push(field.clone());
884 }
885 filter_columns.push(self.filter_column(row.clone()));
886 columns.push(self.table_column(row.clone())).unwrap();
887 let mut table_field_info = object! {};
888 match mode.as_str() {
889 "table" => table_field_info = row.clone(),
890 "array" => table_field_json.push(field.clone()),
891 _ => {}
892 }
893 if !table_field_info.is_empty() {
894 table_fields[field.as_str()] = table_field_info;
895 }
896 }
897
898 let mut tools = self.tools();
899 let db = tools.db.table(table_name);
900 db.where_and(pid_field, "=", pid);
901 if !search.is_empty() {
902 db.where_and(
903 &search_field.join("|"),
904 "like",
905 format!("%{search}%").into(),
906 );
907 }
908
909 for (key, value) in where_or.entries() {
910 if value.is_empty() {
911 continue;
912 }
913 if value.is_array() {
914 db.where_or(key, "in", value.clone());
915 } else if value.is_boolean() {
916 db.where_or(key, "=", value.clone());
917 } else {
918 db.where_or(key, "like", format!("%{value}%").into());
919 }
920 }
921
922 for value in where_and.members() {
923 db.where_and(
924 value[0].as_str().unwrap(),
925 value[1].as_str().unwrap(),
926 value[2].clone(),
927 );
928 }
929
930 let mut db_list = db.clone();
931
932 for item in order.members() {
933 db_list.order(item[0].as_str().unwrap(), item[1].as_bool().unwrap());
934 }
935 let t = alls_field.join(",");
936 db_list.field(t.as_str());
937 if !table_field_json.is_empty() {
938 db_list.json(&table_field_json.join(","));
939 }
940 let mut data = db_list.select();
941
942 let mut table_datas = object! {};
943 let mut ids = HashMap::new();
944
945 for item in data.members_mut() {
946 for (field, _) in table_fields.entries() {
947 if table_datas[field].is_empty() {
948 table_datas[field] = array![];
949 }
950 let _ = table_datas[field].push(item[field].clone());
951 }
952 }
953 for (field, info) in table_fields.entries_mut() {
954 let _ = info["fields"].push("id");
955 let fields_k = info["fields"].members().map(|x| x.as_str().unwrap()).collect::<Vec<&str>>();
956 let table_name = info["table"].clone();
957 let mut data_list = self.tools().db.table(table_name.as_str().unwrap()).where_and("id", "in", table_datas[field].clone()).field(&fields_k.join(",")).select();
958
959 for item in data_list.members_mut() {
960 let id = item["id"].as_str().unwrap_or("").to_string();
961 item.remove("id");
962 let label = item.entries().map(|(_, v)| v.as_str().unwrap()).collect::<Vec<&str>>();
963 ids.insert(
964 id.clone(),
965 object! {
966 value: id.clone(),
967 label:label.join(" | ").clone(),
968 },
969 );
970 }
971 }
972
973 for item in data.members_mut() {
974 for (field, _) in table_fields.entries() {
975 if item[field].is_empty() {
976 continue;
977 }
978 item[field] = ids[item[field].as_str().unwrap()].clone();
979 }
980 }
982
983 object! {
984 data:data,
985 columns:columns,
986 search_name:search_name.join("/"),
987 label:label,
988 btn_all:array![],
989 btn_api:array![],
990 btn_ids:array![]
991 }
992 }
993
994 #[allow(clippy::too_many_arguments)]
1002 #[cfg(any(feature = "sqlite", feature = "mssql", feature = "mysql", feature = "pgsql"))]
1003 fn table_list(
1004 &mut self,
1005 request: JsonValue,
1006 table_name: &str,
1007 fields: JsonValue,
1008 hidd_field: Vec<&str>,
1009 show_field: Vec<&str>,
1010 search_fields: Vec<&str>,
1011 filter_fields: Vec<&str>,
1012 ) -> JsonValue {
1013 self.table().main_table_fields(table_name, fields, hidd_field, show_field).search_fields(search_fields).filter_fields(filter_fields).params(request).get_table()
1014 }
1015 #[allow(clippy::too_many_arguments)]
1023 #[cfg(any(feature = "sqlite", feature = "mssql", feature = "mysql", feature = "pgsql"))]
1024 fn table_edit_list(
1025 &mut self,
1026 request: JsonValue,
1027 table_name: &str,
1028 fields: JsonValue,
1029 hidd_field: Vec<&str>,
1030 show_field: Vec<&str>,
1031 search_fields: Vec<&str>,
1032 edit_fields: Vec<&str>,
1033 ) -> JsonValue {
1034 let page = request["page"].as_i32().unwrap_or(1);
1035 let limit = request["limit"].as_i32().unwrap_or(10);
1036 let search = request["search"].as_str().unwrap_or("");
1037 let order = request["order"].clone();
1038 let where_or = request["where_or"].clone();
1039 let where_and = request["where_and"].clone();
1040 let mut columns = array![];
1041 let mut search_name = vec![];
1042 let mut search_field = vec![];
1043 let mut edit_columns = vec![];
1044
1045 let mut table_fields = object! {};
1047 let mut tree_fields = object! {};
1049
1050 let mut json_fields = vec![];
1052
1053 let mut alls_field = fields.entries().map(|(x, _)| x).collect::<Vec<&str>>();
1054 alls_field.retain(|x| !hidd_field.contains(x));
1055 if !show_field.is_empty() {
1056 alls_field.retain(|x| show_field.contains(x) || *x == "id");
1057 }
1058
1059 for item in alls_field.iter() {
1060 let key = item.to_string();
1061 let field = fields[key.clone()]["field"].as_str().unwrap();
1062 let title = fields[key.clone()]["title"].as_str().unwrap();
1063 let mode = fields[key.clone()]["mode"].as_str().unwrap();
1064
1065 if search_fields.contains(&field) {
1066 search_name.push(title);
1067 search_field.push(field);
1068 }
1069 if edit_fields.contains(&field) {
1070 edit_columns.push(self.filter_column(fields[key.clone()].clone()));
1071 }
1072 columns.push(self.table_column(fields[key.clone()].clone())).unwrap();
1073
1074 match mode {
1075 "table" => {
1076 let table_field_info = if mode == "table" {
1077 fields[key.clone()].clone()
1078 } else {
1079 JsonValue::Null
1080 };
1081 if !table_field_info.is_empty() {
1082 table_fields[field] = table_field_info;
1083 }
1084 }
1085 "tree" => {
1086 let table_field_info = if mode == "tree" {
1087 fields[key.clone()].clone()
1088 } else {
1089 JsonValue::Null
1090 };
1091 if !table_field_info.is_empty() {
1092 tree_fields[field] = table_field_info;
1093 }
1094 }
1095 "object" | "array" | "polygon" => {
1096 json_fields.push(field);
1097 }
1098 _ => {}
1099 }
1100 }
1101
1102 let mut tools = self.tools();
1103 let db = tools.db.table(table_name);
1104
1105 if !search.is_empty() {
1106 db.where_and(
1107 &search_field.join("|"),
1108 "like",
1109 format!("%{search}%").into(),
1110 );
1111 }
1112
1113 for (key, value) in where_or.entries() {
1114 if value.is_empty() {
1115 continue;
1116 }
1117 if value.is_array() {
1118 db.where_or(key, "in", value.clone());
1119 } else if value.is_boolean() {
1120 db.where_or(key, "=", value.clone());
1121 } else {
1122 db.where_or(key, "like", format!("%{value}%").into());
1123 }
1124 }
1125
1126 for value in where_and.members() {
1127 db.where_and(
1128 value[0].as_str().unwrap(),
1129 value[1].as_str().unwrap(),
1130 value[2].clone(),
1131 );
1132 }
1133
1134 let total = db.clone().count().as_f64().unwrap();
1135 let mut db_list = db.clone();
1136
1137 for item in order.members() {
1138 db_list.order(item[0].as_str().unwrap(), item[1].as_bool().unwrap());
1139 }
1140
1141 let t = alls_field.join(",");
1142 db_list.field(t.as_str());
1143 let mut data = db_list.page(page, limit).json(&json_fields.join(",")).select();
1144
1145 let mut table_data = object! {};
1146
1147 for item in data.members_mut() {
1148 if !table_fields.is_empty() {
1149 for (field, field_info) in table_fields.entries() {
1150 if item[field].is_empty() {
1151 continue;
1152 }
1153 let key = item[field].as_str().unwrap_or("");
1154 if key.is_empty() {
1155 continue;
1156 }
1157 if !table_data.has_key(field) {
1158 table_data[field] = object! {};
1159 }
1160 if !table_data[field].has_key(key) {
1161 let mut info = field_info.clone();
1162 let _ = info["fields"].push("id");
1163 let fields_k = info["fields"].members().map(|x| x.as_str().unwrap()).collect::<Vec<&str>>();
1164 let mut find = self.tools().db.table(info["table"].as_str().unwrap()).where_and("id", "=", key.into()).field(&fields_k.join(",")).find();
1165 let id = find["id"].to_string();
1166 find.remove("id");
1167 let label = find.entries().map(|(_, v)| v.to_string()).collect::<Vec<String>>();
1168 table_data[field][key] = object! {
1169 value: id.clone(),
1170 label:label.join(" | ").clone(),
1171 };
1172 }
1173 item[field] = table_data[field][key].clone();
1174 }
1175 }
1176 if !tree_fields.is_empty() {
1177 for (field, field_info) in tree_fields.entries() {
1178 if item[field].is_empty() {
1179 continue;
1180 }
1181 let key = item[field].as_str().unwrap_or("");
1182 if key.is_empty() {
1183 continue;
1184 }
1185 if !table_data.has_key(field) {
1186 table_data[field] = object! {};
1187 }
1188 if !table_data[field].has_key(key) {
1189 let mut info = field_info.clone();
1190 let pid_field = info["pid_field"].clone().to_string();
1191 let _ = info["fields"].push("id");
1192 let _ = info["fields"].push(pid_field.clone());
1193 let fields_k = info["fields"].members().map(|x| x.as_str().unwrap()).collect::<Vec<&str>>();
1194 let mut find = self.tools().db.table(info["table"].as_str().unwrap()).where_and("id", "=", key.into()).field(&fields_k.join(",")).find();
1195 let mut pid = find[pid_field.clone()].to_string();
1196 let mut name_list = vec![];
1197 let id = find["id"].to_string();
1198 find.remove("id");
1199 find.remove(&pid_field);
1200 let label = find.entries().map(|(_, v)| v.to_string()).collect::<Vec<String>>();
1201 name_list.push(label.join(" | ").clone());
1202 loop {
1203 if pid.is_empty() {
1204 break;
1205 }
1206 let mut t = self.tools().db.table(info["table"].as_str().unwrap()).where_and("id", "=", pid.clone().into()).field(&fields_k.join(",")).find();
1207 pid = t[pid_field.clone()].to_string();
1208 t.remove("id");
1209 t.remove(&pid_field);
1210 let label = t.entries().map(|(_, v)| v.to_string()).collect::<Vec<String>>();
1211 name_list.push(label.join(" | ").clone());
1212 }
1213 name_list.reverse();
1214 table_data[field][key] = object! {
1215 value: id.clone(),
1216 label:name_list.join("/").clone(),
1217 };
1218 }
1219 item[field] = table_data[field][key].clone();
1220 }
1221 }
1222 }
1223
1224 object! {
1225 total:total,
1226 data:data,
1227 columns:columns,
1228 search_name:search_name.join("/"),
1229 edit_columns:edit_columns
1230 }
1231 }
1232 #[allow(clippy::too_many_arguments)]
1233 #[cfg(any(feature = "sqlite", feature = "mssql", feature = "mysql", feature = "pgsql"))]
1234 fn table(&mut self) -> Tables {
1235 Tables::new(self.tools().db.clone())
1236 }
1237 fn table_column(&mut self, field: JsonValue) -> JsonValue {
1238 object! {
1239 name:field["field"].clone(),
1240 label:field["title"].clone(),
1241 align:"center",
1242 field:field["field"].clone(),
1243 sortable:true,
1244 mode:field["mode"].clone(),
1245 dec:field["dec"].clone(),
1246 fields:field["fields"].clone()
1247 }
1248 }
1249 fn filter_column(&mut self, field: JsonValue) -> JsonValue {
1250 object! {
1251 name:field["field"].clone(),
1252 label:field["title"].clone(),
1253 mode:field["mode"].clone(),
1254 option:field["option"].clone(),
1255 api:field["api"].clone(),
1256 fields:field["fields"].clone()
1257 }
1258 }
1259 fn tools(&mut self) -> Tools {
1261 let tools = PLUGIN_TOOLS.lock().unwrap();
1262 let tools = tools.get("tools").unwrap().clone();
1263 tools
1264 }
1265 fn config(&mut self, name: &str) -> JsonValue {
1267 if CONFIG.lock().unwrap().get(name).is_none() {
1268 object! {}
1269 } else {
1270 CONFIG.lock().unwrap().get(name).unwrap().clone()
1271 }
1272 }
1273 fn btn(&mut self) -> Btn {
1276 let mut btn = Btn::new(self.api().as_str());
1277 btn.fields(self.params().clone());
1278 btn.tags(self.tags());
1279 btn.icon(self.icon());
1280 btn.desc(self.description());
1281 btn.auth(self.auth());
1282 btn.public(self.public());
1283 btn.title(self.title());
1284 btn.btn_type(BtnType::Api);
1285 btn.btn_color(BtnColor::Primary);
1286 btn.path(self.api().clone().replace(".", "/").as_str());
1287 btn.pass(false);
1288 btn.addon();
1289 btn
1290 }
1291 fn set_global_data(&mut self, key: &str, value: JsonValue) {
1293 GLOBAL_DATA.with(|data| {
1294 data.borrow_mut()[key] = value;
1295 });
1296 }
1297 fn get_global_data(&mut self) -> JsonValue {
1299 GLOBAL_DATA.with(|data| {
1300 data.borrow().clone()
1301 })
1302 }
1303 fn get_global_data_key(&mut self, key: &str) -> JsonValue {
1305 GLOBAL_DATA.with(|data| {
1306 data.borrow()[key].clone()
1307 })
1308 }
1309}
1310#[derive(Debug, Clone)]
1311pub struct Btn {
1312 api: String,
1313 title: String,
1314 desc: String,
1315 tags: &'static [&'static str],
1316 auth: bool,
1317 public: bool,
1318 btn_type: BtnType,
1319 color: BtnColor,
1320 icon: String,
1321 cnd: Vec<JsonValue>,
1322 url: String,
1323 path: String,
1324 fields: JsonValue,
1325 addon: String,
1326 pass: bool,
1328}
1329impl Btn {
1330 pub fn new(api: &str) -> Self {
1331 Self {
1332 api: api.to_string(),
1333 title: "".to_string(),
1334 desc: "".to_string(),
1335 btn_type: BtnType::Api,
1336 color: BtnColor::Primary,
1337 icon: "".to_string(),
1338 auth: false,
1339 public: false,
1340 cnd: vec![],
1341 url: "".to_string(),
1342 path: "".to_string(),
1343 fields: object! {},
1344 tags: &[],
1345 pass: false,
1346 addon: "".to_string(),
1347 }
1348 }
1349
1350 pub fn addon(&mut self) -> &mut Self {
1351 self.addon = self.api.split('.').nth(0).unwrap().to_string();
1352 self
1353 }
1354 pub fn path(&mut self, path: &str) -> &mut Self {
1355 self.path = path.to_string();
1356 self
1357 }
1358 pub fn cnd(&mut self, cnd: Vec<JsonValue>) -> &mut Self {
1359 self.cnd = cnd;
1360 self
1361 }
1362 pub fn btn_type(&mut self, btn_type: BtnType) -> &mut Self {
1363 self.btn_type = btn_type;
1364 self
1365 }
1366 pub fn btn_color(&mut self, btn_color: BtnColor) -> &mut Self {
1367 self.color = btn_color;
1368 self
1369 }
1370 pub fn fields(&mut self, fields: JsonValue) -> &mut Self {
1371 self.fields = fields;
1372 self
1373 }
1374 pub fn pass(&mut self, pass: bool) -> &mut Self {
1375 self.pass = pass;
1376 self
1377 }
1378 pub fn url(&mut self, url: &str) -> &mut Self {
1379 self.url = url.to_string();
1380 self
1381 }
1382 pub fn title(&mut self, title: &str) -> &mut Self {
1383 self.title = title.to_string();
1384 self
1385 }
1386 pub fn desc(&mut self, desc: &str) -> &mut Self {
1387 self.desc = desc.to_string();
1388 self
1389 }
1390 pub fn tags(&mut self, tags: &'static [&'static str]) -> &mut Self {
1391 self.tags = tags;
1392 self
1393 }
1394 pub fn public(&mut self, public: bool) -> &mut Self {
1395 self.public = public;
1396 self
1397 }
1398 pub fn auth(&mut self, auth: bool) -> &mut Self {
1399 self.auth = auth;
1400 self
1401 }
1402 pub fn icon(&mut self, icon: &str) -> &mut Self {
1403 self.icon = icon.to_string();
1404 self
1405 }
1406 pub fn json(&mut self) -> JsonValue {
1407 object! {
1408 addon:self.addon.to_string() ,
1409 api:self.api.clone(),
1410 title:self.title.clone(),
1411 desc:self.desc.clone(),
1412 auth:self.auth,
1413 public:self.public,
1414 btn_type:self.btn_type.clone().str(),
1415 color:self.color.clone().str(),
1416 icon:self.icon.clone(),
1417 cnd:self.cnd.clone(),
1418 url:self.url.clone(),
1419 path:self.path.clone(),
1420 fields:self.fields.clone(),
1421 tags:self.tags,
1422 pass:self.pass,
1423 }
1424 }
1425}
1426
1427#[derive(Debug, Clone)]
1429pub enum InterfaceType {
1430 API,
1431 BTN,
1432 MENU,
1433}
1434
1435impl InterfaceType {
1436 pub fn str(&self) -> &'static str {
1437 match self {
1438 InterfaceType::API => "api",
1439 InterfaceType::BTN => "btn",
1440 InterfaceType::MENU => "menu",
1441 }
1442 }
1443 pub fn types() -> Vec<&'static str> {
1444 vec!["api", "btn", "menu"]
1445 }
1446}
1447
1448#[derive(Debug, Clone)]
1450pub enum BtnType {
1451 Form,
1453 FormDownload,
1455 FormCustom,
1457 FormData,
1458 Url,
1460 Api,
1462 Download,
1464 Path,
1466 DialogCustom,
1468 FormApiDialogCustom,
1470 Preview,
1472}
1473
1474impl BtnType {
1475 fn str(self) -> &'static str {
1476 match self {
1477 BtnType::Form => "form",
1478 BtnType::FormDownload => "form_download",
1479 BtnType::FormCustom => "form_custom",
1480 BtnType::FormData => "form_data",
1481 BtnType::Api => "api",
1482 BtnType::Download => "download",
1483 BtnType::Url => "url",
1484 BtnType::Path => "path",
1485 BtnType::DialogCustom => "dialog_custom",
1486 BtnType::FormApiDialogCustom => "form_api_dialog_custom",
1487 BtnType::Preview => "preview",
1488 }
1489 }
1490}
1491#[derive(Debug, Clone)]
1493pub enum BtnColor {
1494 Primary,
1495 Red,
1496 Blue,
1497 Yellow,
1498 Green,
1499}
1500
1501impl BtnColor {
1502 fn str(self) -> &'static str {
1503 match self {
1504 BtnColor::Primary => "primary",
1505 BtnColor::Red => "negative",
1506 BtnColor::Blue => "info",
1507 BtnColor::Yellow => "warning",
1508 BtnColor::Green => "positive",
1509 }
1510 }
1511}
1512
1513
1514pub struct Dashboard {
1515 title: String,
1517 data: JsonValue,
1519 model: DashboardModel,
1521 class: String,
1523 icon: String,
1525 desc: String,
1527 api: String,
1529 options: JsonValue,
1531}
1532impl Dashboard {
1533 pub fn new(title: &str) -> Dashboard {
1534 Dashboard {
1535 title: title.to_string(),
1536 data: JsonValue::Null,
1537 model: DashboardModel::Number,
1538 class: "col-4".to_string(),
1539 icon: "".to_string(),
1540 desc: "".to_string(),
1541 api: "".to_string(),
1542 options: JsonValue::Null,
1543 }
1544 }
1545 pub fn options(&mut self, options: JsonValue) -> &mut Self {
1546 self.options = options;
1547 self
1548 }
1549 pub fn api(&mut self, api: &str) -> &mut Self {
1550 self.api = api.to_string();
1551 self
1552 }
1553 pub fn data(&mut self, data: JsonValue) -> &mut Self {
1554 self.data = data;
1555 self
1556 }
1557 pub fn class(&mut self, name: &str) -> &mut Self {
1558 self.class = name.to_string();
1559 self
1560 }
1561 pub fn icon(&mut self, name: &str) -> &mut Self {
1562 self.icon = name.to_string();
1563 self
1564 }
1565 pub fn model(&mut self, dashboard_model: DashboardModel) -> &mut Self {
1566 self.model = dashboard_model;
1567 self
1568 }
1569 pub fn desc(&mut self, desc: &str) -> &mut Self {
1570 self.desc = desc.to_string();
1571 self
1572 }
1573 pub fn json(&self) -> JsonValue {
1574 object! {
1575 title: self.title.clone(),
1576 data: self.data.clone(),
1577 class: self.class.clone(),
1578 icon: self.icon.clone(),
1579 model: self.model.str(),
1580 desc: self.desc.clone(),
1581 api: self.api.clone(),
1582 options:self.options.clone(),
1583 }
1584 }
1585}
1586
1587pub enum DashboardModel {
1588 Number,
1590 EchartsBar,
1592 EchartsStackedBar,
1594 EchartsBarRace,
1596 EchartsPie,
1598 EchartsDoughnut,
1600 EchartsStackedLine,
1602 EchartsStackedLineArea,
1604 EchartsGeoGraph,
1606}
1607
1608impl DashboardModel {
1609 pub fn str(&self) -> &'static str {
1610 match self {
1611 Self::Number => "number",
1612 Self::EchartsBar => "echarts-bar",
1613 Self::EchartsStackedBar => "echarts-stacked_bar",
1614 Self::EchartsBarRace => "echarts-bar_race",
1615 Self::EchartsPie => "echarts-pie",
1616 Self::EchartsDoughnut => "echarts-doughnut",
1617 Self::EchartsStackedLine => "echarts-stacked_line",
1618 Self::EchartsStackedLineArea => "echarts-stacked_line_area",
1619 Self::EchartsGeoGraph => "echarts-geo_graph",
1620 }
1621 }
1622}