oa-leave-kit-runtime.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import {
  2. calcLeaveHours,
  3. DEFAULT_LEAVE_WORK_HOURS,
  4. type LeaveWorkHours
  5. } from '@/utils/oa-leave-hours'
  6. export const LEAVE_MANUAL_PREFIX = '__manual_'
  7. export type LeaveKitBinding = {
  8. startField: string
  9. endField: string
  10. durationField: string
  11. workHours: LeaveWorkHours
  12. }
  13. function asRules(schema: unknown): Record<string, unknown>[] {
  14. if (Array.isArray(schema)) return schema.filter((r) => r && typeof r === 'object') as any
  15. if (schema && typeof schema === 'object') {
  16. const s = schema as any
  17. const rules = s.rule || s.rules || s.fields
  18. if (Array.isArray(rules)) return rules.filter((r: any) => r && typeof r === 'object')
  19. if (s.type || s.field) return [s]
  20. }
  21. return []
  22. }
  23. function walk(rules: Record<string, unknown>[], out: Record<string, unknown>[]) {
  24. for (const r of rules) {
  25. out.push(r)
  26. const children = r.children
  27. if (Array.isArray(children)) {
  28. walk(
  29. children.filter((c) => c && typeof c === 'object') as Record<string, unknown>[],
  30. out
  31. )
  32. }
  33. const props = r.props as Record<string, unknown> | undefined
  34. if (props && Array.isArray(props.rule)) {
  35. walk(
  36. props.rule.filter((c) => c && typeof c === 'object') as Record<string, unknown>[],
  37. out
  38. )
  39. }
  40. }
  41. }
  42. function resolveFields(kit: Record<string, unknown>): LeaveKitBinding | null {
  43. const props = (kit.props && typeof kit.props === 'object' ? kit.props : {}) as Record<
  44. string,
  45. any
  46. >
  47. let startField = props.startField as string | undefined
  48. let endField = props.endField as string | undefined
  49. let durationField = props.durationField as string | undefined
  50. const children = (
  51. Array.isArray(kit.children)
  52. ? kit.children
  53. : Array.isArray(props.rule)
  54. ? props.rule
  55. : []
  56. ) as Record<string, any>[]
  57. if (!startField || !endField || !durationField) {
  58. const dateFields: string[] = []
  59. let dur: string | undefined
  60. for (const c of children) {
  61. if (!c?.field) continue
  62. const t = String(c.type || '')
  63. if (t === 'datePicker' || t === 'date' || t === 'timePicker' || t === 'time') {
  64. dateFields.push(String(c.field))
  65. } else if ((t === 'inputNumber' || t === 'number') && !dur) {
  66. dur = String(c.field)
  67. }
  68. }
  69. startField = startField || dateFields[0]
  70. endField = endField || dateFields[1]
  71. durationField = durationField || dur
  72. }
  73. if (!startField || !endField || !durationField) return null
  74. return {
  75. startField,
  76. endField,
  77. durationField,
  78. workHours: {
  79. morningStart: String(props.morningStart || DEFAULT_LEAVE_WORK_HOURS.morningStart),
  80. morningEnd: String(props.morningEnd || DEFAULT_LEAVE_WORK_HOURS.morningEnd),
  81. afternoonStart: String(props.afternoonStart || DEFAULT_LEAVE_WORK_HOURS.afternoonStart),
  82. afternoonEnd: String(props.afternoonEnd || DEFAULT_LEAVE_WORK_HOURS.afternoonEnd)
  83. }
  84. }
  85. }
  86. export function listLeaveKitBindings(schema: unknown): LeaveKitBinding[] {
  87. const flat: Record<string, unknown>[] = []
  88. walk(asRules(schema), flat)
  89. const out: LeaveKitBinding[] = []
  90. for (const r of flat) {
  91. if (String(r.type || '') !== 'leaveKit') continue
  92. const b = resolveFields(r)
  93. if (b) out.push(b)
  94. }
  95. return out
  96. }
  97. /** 未手改时写回时长;返回是否有变更 */
  98. export function applyLeaveKitDurationsToFormData(
  99. schema: unknown,
  100. formData: Record<string, unknown>
  101. ): { data: Record<string, unknown>; changed: boolean } {
  102. const data = { ...formData }
  103. let changed = false
  104. for (const kit of listLeaveKitBindings(schema)) {
  105. const manualKey = LEAVE_MANUAL_PREFIX + kit.durationField
  106. if (data[manualKey]) continue
  107. const hours = calcLeaveHours(
  108. String(data[kit.startField] || ''),
  109. String(data[kit.endField] || ''),
  110. kit.workHours
  111. )
  112. if (hours == null) continue
  113. if (data[kit.durationField] !== hours) {
  114. data[kit.durationField] = hours
  115. changed = true
  116. }
  117. }
  118. return { data, changed }
  119. }
  120. export function markLeaveDurationManual(
  121. formData: Record<string, unknown>,
  122. durationField: string,
  123. manual = true
  124. ) {
  125. const data = { ...formData }
  126. const key = LEAVE_MANUAL_PREFIX + durationField
  127. if (manual) data[key] = true
  128. else delete data[key]
  129. return data
  130. }
  131. export function isLeaveDurationField(schema: unknown, field: string): boolean {
  132. return listLeaveKitBindings(schema).some((k) => k.durationField === field)
  133. }
  134. export function findLeaveKitByStartOrEnd(
  135. schema: unknown,
  136. field: string
  137. ): LeaveKitBinding | null {
  138. return (
  139. listLeaveKitBindings(schema).find(
  140. (k) => k.startField === field || k.endField === field
  141. ) || null
  142. )
  143. }
  144. /**
  145. * 运行时补齐请假套件子规则:时长数字框默认左对齐(覆盖 Element Plus 默认居中)。
  146. * 对已发布且未带 align 的旧 schema 同样生效。
  147. */
  148. export function ensureLeaveKitRuleProps(rules: unknown): any[] {
  149. if (!Array.isArray(rules)) return []
  150. return rules.map((raw) => {
  151. if (!raw || typeof raw !== 'object') return raw
  152. const rule: any = { ...(raw as object) }
  153. if (String(rule.type || '') === 'leaveKit') {
  154. const binding = resolveFields(rule)
  155. const patchChildren = (list: any[]): any[] =>
  156. list.map((c) => {
  157. if (!c || typeof c !== 'object') return c
  158. const child: any = { ...c }
  159. const t = String(child.type || '')
  160. const isDuration =
  161. (binding && child.field === binding.durationField) ||
  162. ((t === 'inputNumber' || t === 'number') &&
  163. String(child.title || '').includes('时长'))
  164. if (isDuration) {
  165. child.props = { ...(child.props || {}), align: 'left' }
  166. const cls = String(child.class || child.className || '')
  167. if (!cls.includes('oa-leave-duration')) {
  168. child.class = (cls ? cls + ' ' : '') + 'oa-leave-duration'
  169. }
  170. }
  171. if (Array.isArray(child.children)) {
  172. child.children = patchChildren(child.children)
  173. }
  174. return child
  175. })
  176. if (Array.isArray(rule.children)) {
  177. rule.children = patchChildren(rule.children)
  178. }
  179. if (Array.isArray(rule.props?.rule)) {
  180. rule.props = {
  181. ...rule.props,
  182. rule: patchChildren(rule.props.rule)
  183. }
  184. }
  185. }
  186. if (Array.isArray(rule.children)) {
  187. rule.children = ensureLeaveKitRuleProps(rule.children)
  188. }
  189. if (Array.isArray(rule.props?.rule)) {
  190. rule.props = {
  191. ...rule.props,
  192. rule: ensureLeaveKitRuleProps(rule.props.rule)
  193. }
  194. }
  195. return rule
  196. })
  197. }