oa_datetime_normalize.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. """OA 日期时间:PC / 小程序 valueFormat 对齐与历史到分数据兼容。
  2. PC FormCreate(Element)在 datetime 未配置 valueFormat 时默认为
  3. ``YYYY-MM-DD HH:mm:ss``;小程序曾默认到分,导致回显空白。
  4. 本模块:对「期望到秒」的字段,将 ``YYYY-MM-DD HH:mm`` 补 ``:00``。
  5. 请假/加班等显式配置到分的字段不受影响。
  6. """
  7. from __future__ import annotations
  8. import re
  9. from typing import Any, Dict, List, Optional
  10. _MINUTE_ONLY_RE = re.compile(r"^(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2})$")
  11. def _extract_rules(form_schema: Any) -> List[Any]:
  12. if form_schema is None:
  13. return []
  14. if isinstance(form_schema, list):
  15. return form_schema
  16. if isinstance(form_schema, dict):
  17. rule = form_schema.get("rule")
  18. if isinstance(rule, list):
  19. return rule
  20. return []
  21. def _walk_rules(rules: List[Any], out: Optional[List[dict]] = None) -> List[dict]:
  22. out = out if out is not None else []
  23. for r in rules or []:
  24. if not isinstance(r, dict):
  25. continue
  26. out.append(r)
  27. children = r.get("children")
  28. if isinstance(children, list):
  29. _walk_rules(children, out)
  30. props = r.get("props") or {}
  31. nested = props.get("rule") if isinstance(props, dict) else None
  32. if isinstance(nested, list):
  33. _walk_rules(nested, out)
  34. return out
  35. def _effective_value_format(rule: dict) -> str:
  36. props = rule.get("props") if isinstance(rule.get("props"), dict) else {}
  37. explicit = str(props.get("valueFormat") or props.get("format") or "").strip()
  38. if explicit:
  39. return explicit
  40. picker = str(props.get("type") or "date").lower()
  41. if picker in ("datetime", "datetimerange"):
  42. return "YYYY-MM-DD HH:mm:ss"
  43. if picker in ("year",):
  44. return "YYYY"
  45. if picker in ("month", "monthrange"):
  46. return "YYYY-MM"
  47. return "YYYY-MM-DD"
  48. def _expects_seconds(fmt: str) -> bool:
  49. f = str(fmt or "")
  50. return "ss" in f or "SS" in f
  51. def _pad_one(val: Any) -> Any:
  52. if not isinstance(val, str):
  53. return val
  54. s = val.strip()
  55. m = _MINUTE_ONLY_RE.match(s)
  56. if not m:
  57. return val
  58. return f"{m.group(1)} {m.group(2)}:00"
  59. def _pad_value(val: Any) -> Any:
  60. if isinstance(val, list):
  61. return [_pad_one(x) for x in val]
  62. return _pad_one(val)
  63. def normalize_datetime_form_values(
  64. form_schema: Any, form_data: Optional[Dict[str, Any]]
  65. ) -> Dict[str, Any]:
  66. """对期望到秒的 datePicker 字段,将到分字符串补全为到秒。返回新 dict,不改入参。"""
  67. data = dict(form_data or {})
  68. for rule in _walk_rules(_extract_rules(form_schema)):
  69. typ = str(rule.get("type") or "").lower()
  70. if typ not in ("datepicker", "fcdatepicker", "date"):
  71. continue
  72. field = rule.get("field")
  73. if not field or field not in data:
  74. continue
  75. fmt = _effective_value_format(rule)
  76. if not _expects_seconds(fmt):
  77. continue
  78. data[field] = _pad_value(data.get(field))
  79. return data