@veltra/desktop 1.1.15 → 1.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. package/dist/components/action/action-group.vue.d.ts +2 -2
  2. package/dist/components/action/action-group.vue_vue_type_script_setup_true_lang.js.map +1 -1
  3. package/dist/components/action/action.js.map +1 -1
  4. package/dist/components/action/action.vue.d.ts +1 -1
  5. package/dist/components/action/style2.css +0 -4
  6. package/dist/components/auto-complete/auto-complete.js.map +1 -1
  7. package/dist/components/auto-complete/style.js +1 -1
  8. package/dist/components/auto-complete/use-keyboard.js.map +1 -1
  9. package/dist/components/auto-complete/use-suggestions.js.map +1 -1
  10. package/dist/components/badge/badge.js +1 -1
  11. package/dist/components/badge/badge.js.map +1 -1
  12. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  13. package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
  14. package/dist/components/batch-edit/batch-edit.js.map +1 -1
  15. package/dist/components/batch-edit/helper.js.map +1 -1
  16. package/dist/components/batch-edit/style.js +1 -1
  17. package/dist/components/batch-edit/use-edit.js.map +1 -1
  18. package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
  19. package/dist/components/button/button-group.js.map +1 -1
  20. package/dist/components/button/button.js.map +1 -1
  21. package/dist/components/calendar/calendar.js.map +1 -1
  22. package/dist/components/calendar/utils.js.map +1 -1
  23. package/dist/components/card/card-action.js.map +1 -1
  24. package/dist/components/card/card-content.js.map +1 -1
  25. package/dist/components/card/card-cover.js.map +1 -1
  26. package/dist/components/card/card-header.js.map +1 -1
  27. package/dist/components/card/card.js.map +1 -1
  28. package/dist/components/cascade/cascade-panel-item.js.map +1 -1
  29. package/dist/components/cascade/cascade.js.map +1 -1
  30. package/dist/components/cascade/cascade.vue.d.ts +3 -3
  31. package/dist/components/cascade/node.js.map +1 -1
  32. package/dist/components/cascade/use-check.js.map +1 -1
  33. package/dist/components/cascade/use-data-map.js.map +1 -1
  34. package/dist/components/cascade/use-select.js +1 -1
  35. package/dist/components/cascade/use-select.js.map +1 -1
  36. package/dist/components/check-tag/check-tag.js.map +1 -1
  37. package/dist/components/checkbox/checkbox-button.js.map +1 -1
  38. package/dist/components/checkbox/checkbox-button.vue.d.ts +1 -1
  39. package/dist/components/checkbox/checkbox.js.map +1 -1
  40. package/dist/components/checkbox/style.js +1 -1
  41. package/dist/components/checkbox-group/checkbox-group.js.map +1 -1
  42. package/dist/components/code-editor/code-editor.js.map +1 -1
  43. package/dist/components/collapse/collapse-item.js.map +1 -1
  44. package/dist/components/collapse/collapse.js.map +1 -1
  45. package/dist/components/condition-editor/components/condition-group.js +138 -103
  46. package/dist/components/condition-editor/components/condition-group.js.map +1 -1
  47. package/dist/components/condition-editor/components/condition-row.js +146 -95
  48. package/dist/components/condition-editor/components/condition-row.js.map +1 -1
  49. package/dist/components/condition-editor/condition-editor.js +18 -40
  50. package/dist/components/condition-editor/condition-editor.js.map +1 -1
  51. package/dist/components/condition-editor/condition-editor.vue.d.ts +0 -2
  52. package/dist/components/condition-editor/core/evaluator.d.ts +22 -0
  53. package/dist/components/condition-editor/core/evaluator.js +111 -50
  54. package/dist/components/condition-editor/core/evaluator.js.map +1 -1
  55. package/dist/components/condition-editor/core/operators.js.map +1 -1
  56. package/dist/components/condition-editor/index.d.ts +2 -1
  57. package/dist/components/condition-editor/style2.css +159 -82
  58. package/dist/components/context-menu/api.js.map +1 -1
  59. package/dist/components/context-menu/context-menu-item.js.map +1 -1
  60. package/dist/components/context-menu/context-menu.js.map +1 -1
  61. package/dist/components/context-menu/style.js +1 -1
  62. package/dist/components/date-panel/date-panel.js.map +1 -1
  63. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  64. package/dist/components/date-panel/headers/day.js.map +1 -1
  65. package/dist/components/date-panel/headers/month.js.map +1 -1
  66. package/dist/components/date-panel/headers/year.js.map +1 -1
  67. package/dist/components/date-panel/panels/day.js.map +1 -1
  68. package/dist/components/date-panel/panels/month.js.map +1 -1
  69. package/dist/components/date-panel/panels/year.js.map +1 -1
  70. package/dist/components/date-panel/use-date-select.js.map +1 -1
  71. package/dist/components/date-panel/use-panel-date.js.map +1 -1
  72. package/dist/components/date-panel/use-panel.js.map +1 -1
  73. package/dist/components/date-picker/date-picker.js.map +1 -1
  74. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  75. package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
  76. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  77. package/dist/components/dialog/dialog.js.map +1 -1
  78. package/dist/components/dialog/style.js +2 -2
  79. package/dist/components/dialog/use-maximum.js.map +1 -1
  80. package/dist/components/drawer/drawer.js.map +1 -1
  81. package/dist/components/drawer/style.js +1 -1
  82. package/dist/components/dropdown/dropdown.js +1 -0
  83. package/dist/components/dropdown/dropdown.js.map +1 -1
  84. package/dist/components/dropdown/style.js +1 -1
  85. package/dist/components/empty/empty.js.map +1 -1
  86. package/dist/components/expression-editor/components/path-preview.js.map +1 -1
  87. package/dist/components/expression-editor/components/variable-picker.js.map +1 -1
  88. package/dist/components/expression-editor/core/chip.js.map +1 -1
  89. package/dist/components/expression-editor/core/editor.js.map +1 -1
  90. package/dist/components/expression-editor/core/mention.js.map +1 -1
  91. package/dist/components/expression-editor/core/model.js.map +1 -1
  92. package/dist/components/expression-editor/di.js.map +1 -1
  93. package/dist/components/expression-editor/expression-editor.js.map +1 -1
  94. package/dist/components/file-picker/file-picker.js.map +1 -1
  95. package/dist/components/file-picker/helper.js.map +1 -1
  96. package/dist/components/file-viewer/file-viewer.js.map +1 -1
  97. package/dist/components/file-viewer/helper.js.map +1 -1
  98. package/dist/components/file-viewer/previewers/docx-previewer.js.map +1 -1
  99. package/dist/components/file-viewer/previewers/image-previewer.js.map +1 -1
  100. package/dist/components/file-viewer/previewers/pdf-previewer.js.map +1 -1
  101. package/dist/components/file-viewer/previewers/pdf-viewport-scroll.js.map +1 -1
  102. package/dist/components/file-viewer/previewers/pdf-zoom-bridge.js.map +1 -1
  103. package/dist/components/file-viewer/previewers/sheet-previewer.js.map +1 -1
  104. package/dist/components/file-viewer/previewers/text-previewer.js.map +1 -1
  105. package/dist/components/file-viewer/previewers/video-previewer.js.map +1 -1
  106. package/dist/components/float-button/float-button.js.map +1 -1
  107. package/dist/components/form/dynamic-form-model.js.map +1 -1
  108. package/dist/components/form/form-model.js.map +1 -1
  109. package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js.map +1 -1
  110. package/dist/components/form/helper.js.map +1 -1
  111. package/dist/components/form/use-node-interceptor.js.map +1 -1
  112. package/dist/components/form-item/form-item.js.map +1 -1
  113. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js +2 -1
  114. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js.map +1 -1
  115. package/dist/components/gantt-chart/gantt-chart.js.map +1 -1
  116. package/dist/components/grid/breakpoint.js.map +1 -1
  117. package/dist/components/grid/grid-item.js.map +1 -1
  118. package/dist/components/grid/grid.js.map +1 -1
  119. package/dist/components/grid/use-responsive.js.map +1 -1
  120. package/dist/components/grid-input/grid-input.js.map +1 -1
  121. package/dist/components/group-input/group-input.js.map +1 -1
  122. package/dist/components/group-input/use-group-items.js.map +1 -1
  123. package/dist/components/icon/icon.js.map +1 -1
  124. package/dist/components/index.d.ts +1 -0
  125. package/dist/components/index.js +4 -0
  126. package/dist/components/input/input.vue_vue_type_script_setup_true_lang.js.map +1 -1
  127. package/dist/components/input/style.js +1 -1
  128. package/dist/components/kbd/kbd.js.map +1 -1
  129. package/dist/components/layout/layout-resizer.js.map +1 -1
  130. package/dist/components/layout/layout.js.map +1 -1
  131. package/dist/components/list/list-item.js.map +1 -1
  132. package/dist/components/list/list.js.map +1 -1
  133. package/dist/components/loading/directive.js.map +1 -1
  134. package/dist/components/loading/loading.js.map +1 -1
  135. package/dist/components/loading/style.js +1 -1
  136. package/dist/components/menu/helper.js.map +1 -1
  137. package/dist/components/menu/menu-item-collapsed.js.map +1 -1
  138. package/dist/components/menu/menu-item.js.map +1 -1
  139. package/dist/components/menu/menu-sub-collapsed.js.map +1 -1
  140. package/dist/components/menu/menu-sub.js.map +1 -1
  141. package/dist/components/menu/menu.js.map +1 -1
  142. package/dist/components/menu/use-menu-item.js.map +1 -1
  143. package/dist/components/menu/walk-menu-path.js.map +1 -1
  144. package/dist/components/message/helper.js.map +1 -1
  145. package/dist/components/message/message-box.js.map +1 -1
  146. package/dist/components/message/message.js.map +1 -1
  147. package/dist/components/message/message2.js.map +1 -1
  148. package/dist/components/message-confirm/message-confirm.js.map +1 -1
  149. package/dist/components/message-confirm/message-confirm2.js.map +1 -1
  150. package/dist/components/multi-select/multi-select-option.js.map +1 -1
  151. package/dist/components/multi-select/multi-select.js.map +1 -1
  152. package/dist/components/multi-select/multi-select.vue.d.ts +2 -2
  153. package/dist/components/multi-select/style.js +1 -1
  154. package/dist/components/multi-tree-select/multi-tree-select.js.map +1 -1
  155. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +2 -2
  156. package/dist/components/multi-tree-select/style.js +1 -1
  157. package/dist/components/node-render/node-render.js.map +1 -1
  158. package/dist/components/notification/index.js.map +1 -1
  159. package/dist/components/notification/notification.js.map +1 -1
  160. package/dist/components/number/number.js.map +1 -1
  161. package/dist/components/number-input/number-input.js.map +1 -1
  162. package/dist/components/number-range-input/number-range-input.js.map +1 -1
  163. package/dist/components/paginator/paginator.js.map +1 -1
  164. package/dist/components/palette/color-transform.js.map +1 -1
  165. package/dist/components/palette/palette-alpha.js.map +1 -1
  166. package/dist/components/palette/palette-color-switch.js.map +1 -1
  167. package/dist/components/palette/palette-hue.js.map +1 -1
  168. package/dist/components/palette/palette-sv.js.map +1 -1
  169. package/dist/components/palette/palette.js.map +1 -1
  170. package/dist/components/palette/use-hsv.js.map +1 -1
  171. package/dist/components/password-input/password-input.js.map +1 -1
  172. package/dist/components/pop-confirm/pop-confirm.js.map +1 -1
  173. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
  174. package/dist/components/progress/progress.js.map +1 -1
  175. package/dist/components/progress-nodes/progress-nodes.js.map +1 -1
  176. package/dist/components/radio/radio.js.map +1 -1
  177. package/dist/components/radio/style.js +1 -1
  178. package/dist/components/radio-group/radio-group.js.map +1 -1
  179. package/dist/components/rich-text-editor/rich-text-editor.js.map +1 -1
  180. package/dist/components/rich-text-editor/toolbar.js.map +1 -1
  181. package/dist/components/scroll/scroll-bar.js +2 -0
  182. package/dist/components/scroll/scroll-bar.js.map +1 -1
  183. package/dist/components/scroll/scroll.js.map +1 -1
  184. package/dist/components/select/select.js.map +1 -1
  185. package/dist/components/select/select.vue.d.ts +2 -2
  186. package/dist/components/select/use-keyboard.js.map +1 -1
  187. package/dist/components/select/use-options.js.map +1 -1
  188. package/dist/components/slider/slider-thumb.js.map +1 -1
  189. package/dist/components/slider/slider.js +1 -1
  190. package/dist/components/slider/slider.js.map +1 -1
  191. package/dist/components/slider/use-slider.js.map +1 -1
  192. package/dist/components/steps/steps.js.map +1 -1
  193. package/dist/components/steps/steps.vue.d.ts +3 -3
  194. package/dist/components/switch/switch.js.map +1 -1
  195. package/dist/components/table/di.js.map +1 -1
  196. package/dist/components/table/node/col.js.map +1 -1
  197. package/dist/components/table/node/row.js.map +1 -1
  198. package/dist/components/table/table-body.js.map +1 -1
  199. package/dist/components/table/table-cell.js.map +1 -1
  200. package/dist/components/table/table-foot.js.map +1 -1
  201. package/dist/components/table/table-head.js.map +1 -1
  202. package/dist/components/table/table-row.js.map +1 -1
  203. package/dist/components/table/table.js.map +1 -1
  204. package/dist/components/table/use-check.js.map +1 -1
  205. package/dist/components/table/use-col-resize.js.map +1 -1
  206. package/dist/components/table/use-columns.js.map +1 -1
  207. package/dist/components/table/use-fixed-columns.js.map +1 -1
  208. package/dist/components/table/use-rows.js.map +1 -1
  209. package/dist/components/table/use-table.js.map +1 -1
  210. package/dist/components/table-editor/table-editor.js.map +1 -1
  211. package/dist/components/tabs/style.js +1 -1
  212. package/dist/components/tabs/tabs-horizontal.js.map +1 -1
  213. package/dist/components/tabs/tabs-horizontal.vue.d.ts +2 -2
  214. package/dist/components/tabs/tabs-vertical.js.map +1 -1
  215. package/dist/components/tabs/tabs-vertical.vue.d.ts +2 -2
  216. package/dist/components/tabs/tabs.js.map +1 -1
  217. package/dist/components/tabs/tabs.vue.d.ts +2 -2
  218. package/dist/components/tabs/use-tabs-bar.js.map +1 -1
  219. package/dist/components/tag/tag.js.map +1 -1
  220. package/dist/components/text/text.js.map +1 -1
  221. package/dist/components/textarea/style.js +1 -1
  222. package/dist/components/textarea/textarea.js.map +1 -1
  223. package/dist/components/textarea/textarea.vue.d.ts +1 -1
  224. package/dist/components/textarea/utils.js.map +1 -1
  225. package/dist/components/theme/schema.js.map +1 -1
  226. package/dist/components/theme/theme.js.map +1 -1
  227. package/dist/components/tip/tip.js.map +1 -1
  228. package/dist/components/tip/tip.vue.d.ts +1 -1
  229. package/dist/components/tip/use-nest.js.map +1 -1
  230. package/dist/components/tree/tree-node.js.map +1 -1
  231. package/dist/components/tree/tree-node2.js.map +1 -1
  232. package/dist/components/tree/tree.js.map +1 -1
  233. package/dist/components/tree/tree.vue.d.ts +1 -1
  234. package/dist/components/tree/use-check.js.map +1 -1
  235. package/dist/components/tree/use-filter.js.map +1 -1
  236. package/dist/components/tree/use-select.js.map +1 -1
  237. package/dist/components/tree/use-tree-nodes.js.map +1 -1
  238. package/dist/components/tree-select/tree-select.js.map +1 -1
  239. package/dist/components/tree-select/tree-select.vue.d.ts +2 -2
  240. package/dist/components/watermark/watermark.js.map +1 -1
  241. package/dist/index.d.ts +4 -3
  242. package/dist/index.js +2 -1
  243. package/dist/install.js.map +1 -1
  244. package/dist/types/batch-edit.d.ts +2 -2
  245. package/dist/types/condition-editor.d.ts +21 -9
  246. package/dist/types/index.d.ts +2 -2
  247. package/package.json +13 -16
  248. package/src/components/action/style.scss +0 -6
  249. package/src/components/auto-complete/style.scss +0 -1
  250. package/src/components/auto-complete/style.ts +1 -1
  251. package/src/components/badge/badge.vue +1 -1
  252. package/src/components/batch-edit/style.ts +1 -1
  253. package/src/components/button/style.scss +1 -1
  254. package/src/components/cascade/use-select.ts +8 -6
  255. package/src/components/checkbox/style.ts +1 -1
  256. package/src/components/collapse/__test__/collapse.test.ts +3 -3
  257. package/src/components/condition-editor/components/condition-group.vue +118 -87
  258. package/src/components/condition-editor/components/condition-row.vue +137 -89
  259. package/src/components/condition-editor/condition-editor.vue +19 -48
  260. package/src/components/condition-editor/core/evaluator.ts +143 -57
  261. package/src/components/condition-editor/index.ts +6 -0
  262. package/src/components/condition-editor/style.scss +189 -102
  263. package/src/components/context-menu/style.ts +1 -1
  264. package/src/components/dialog/style.ts +2 -2
  265. package/src/components/drawer/style.ts +1 -1
  266. package/src/components/dropdown/style.ts +1 -1
  267. package/src/components/expression-editor/style.scss +1 -1
  268. package/src/components/form-item/form-item.vue +4 -9
  269. package/src/components/input/style.ts +1 -1
  270. package/src/components/loading/style.ts +1 -1
  271. package/src/components/menu/style.scss +1 -1
  272. package/src/components/message/style.scss +1 -1
  273. package/src/components/multi-select/style.ts +1 -1
  274. package/src/components/multi-tree-select/style.ts +1 -1
  275. package/src/components/number-input/__test__/number-input.test.ts +1 -1
  276. package/src/components/radio/style.ts +1 -1
  277. package/src/components/slider/slider.vue +1 -1
  278. package/src/components/table/di.ts +1 -1
  279. package/src/components/tabs/style.ts +1 -1
  280. package/src/components/textarea/style.ts +1 -1
  281. package/src/types/batch-edit.ts +2 -2
  282. package/src/types/condition-editor.ts +22 -8
@@ -27,13 +27,13 @@
27
27
  <script lang="ts" setup>
28
28
  import { useFormFallbackProps } from '@veltra/compositions'
29
29
  import { bem, injectFormContext } from '@veltra/utils'
30
- import { computed, nextTick, shallowRef, useTemplateRef, watch } from 'vue'
30
+ import { computed, shallowRef, useTemplateRef, watch } from 'vue'
31
31
 
32
32
  import type { ConditionEditorProps, ConditionExpression } from '../../types'
33
33
  import VariablePicker from '../expression-editor/components/variable-picker.vue'
34
34
  import ConditionGroup from './components/condition-group.vue'
35
35
  import type { MentionPayload } from './components/condition-row.vue'
36
- import { createEmptyGroup, evaluate } from './core/evaluator'
36
+ import { createEmptyGroup } from './core/evaluator'
37
37
 
38
38
  defineOptions({ name: 'ConditionEditor' })
39
39
 
@@ -43,7 +43,6 @@ const props = withDefaults(defineProps<ConditionEditorProps>(), {
43
43
 
44
44
  const emit = defineEmits<{
45
45
  (e: 'update:modelValue', value: ConditionExpression): void
46
- (e: 'evaluate', results: ConditionExpression): void
47
46
  }>()
48
47
 
49
48
  const cls = bem('condition-editor')
@@ -58,34 +57,29 @@ const className = computed(() => [
58
57
  ])
59
58
 
60
59
  const rootGroup = shallowRef<ConditionExpression>(
61
- props.modelValue ? JSON.parse(JSON.stringify(props.modelValue)) : createEmptyGroup()
60
+ props.modelValue ? clone(props.modelValue) : createEmptyGroup()
62
61
  )
63
62
 
64
- function serialize(): ConditionExpression {
65
- function clean(group: ConditionExpression): ConditionExpression {
66
- return {
67
- logic: group.logic,
68
- conditions: group.conditions.map((c) => {
69
- const { _result, ...rest } = c
70
- return rest
71
- }),
72
- groups: group.groups.map(clean).filter((g) => g.conditions.length > 0 || g.groups.length > 0)
73
- }
74
- }
75
- return clean(JSON.parse(JSON.stringify(rootGroup.value)))
63
+ function clone<T>(v: T): T {
64
+ return JSON.parse(JSON.stringify(v)) as T
76
65
  }
77
66
 
78
- function emitUpdate() {
79
- emit('update:modelValue', serialize())
67
+ function onGroupUpdate(group: ConditionExpression) {
68
+ rootGroup.value = group
69
+ emit('update:modelValue', clone(group))
80
70
  }
81
71
 
82
- function runEvaluate() {
83
- if (!props.data) return
84
- const cloned = JSON.parse(JSON.stringify(rootGroup.value)) as ConditionExpression
85
- const result = evaluate(cloned, props.data)
86
- rootGroup.value = result
87
- emit('evaluate', serialize())
88
- }
72
+ watch(
73
+ () => props.modelValue,
74
+ (v) => {
75
+ if (!v) {
76
+ rootGroup.value = createEmptyGroup()
77
+ return
78
+ }
79
+ if (JSON.stringify(v) === JSON.stringify(rootGroup.value)) return
80
+ rootGroup.value = clone(v)
81
+ }
82
+ )
89
83
 
90
84
  // ── VariablePicker ──
91
85
 
@@ -121,35 +115,12 @@ function onPickerVisibleChange(v: boolean) {
121
115
  }
122
116
  }
123
117
 
124
- function onGroupUpdate(group: ConditionExpression) {
125
- rootGroup.value = group
126
- emitUpdate()
127
- void nextTick(() => runEvaluate())
128
- }
129
-
130
- watch(
131
- () => props.modelValue,
132
- (v) => {
133
- if (!v) return
134
- const current = serialize()
135
- if (JSON.stringify(v) === JSON.stringify(current)) return
136
- rootGroup.value = JSON.parse(JSON.stringify(v))
137
- }
138
- )
139
-
140
- watch(
141
- () => props.data,
142
- () => runEvaluate(),
143
- { deep: true }
144
- )
145
-
146
118
  function onKeydown(e: KeyboardEvent) {
147
119
  if (disabled.value || readonly.value) return
148
120
  if (pickerVisible.value) {
149
121
  const handled = pickerRef.value?.handleKeydown(e)
150
122
  if (handled) {
151
123
  e.preventDefault()
152
- return
153
124
  }
154
125
  }
155
126
  }
@@ -1,93 +1,179 @@
1
- import type { ConditionExpression, ConditionGroup, ConditionItem } from '../../../types'
1
+ import { o } from '@cat-kit/core'
2
2
 
3
- function resolveValue(item: ConditionItem, data: Record<string, unknown>): string {
4
- if (item.value.kind === 'constant') return item.value.value
5
- return String(getByPath(data, item.value.name) ?? '')
3
+ import type {
4
+ ConditionConnector,
5
+ ConditionExpression,
6
+ ConditionField,
7
+ ConditionGroup,
8
+ ConditionLeaf,
9
+ ConditionNode,
10
+ ConditionValue
11
+ } from '../../../types'
12
+
13
+ export interface EvaluateOptions {
14
+ /** 字段定义 —— 提供后按字段类型做类型感知比较 */
15
+ fields?: ConditionField[]
16
+ /** 上下文数据,用于解析 `kind: 'variable'` 引用 */
17
+ data?: Record<string, unknown>
18
+ }
19
+
20
+ /**
21
+ * 求值表达式
22
+ *
23
+ * - 纯函数,不修改入参
24
+ * - 空表达式(无任何叶子节点)返回 `true`(视作"无限制")
25
+ * - 不完整的叶子(field/operator 缺失、或缺少所需值且 operator 需要值)按 `false` 计
26
+ * - 同组内 AND/OR 按 **从左到右、等优先级** 求值;需要更高优先级请用子分组
27
+ */
28
+ export function evaluate(expression: ConditionExpression, options: EvaluateOptions = {}): boolean {
29
+ return evaluateGroup(expression, options)
6
30
  }
7
31
 
8
- function resolveCompareValue(item: ConditionItem, data: Record<string, unknown>): string {
9
- if (item.value.kind === 'constant') return item.value.value
10
- return String(getByPath(data, item.value.name) ?? '')
32
+ function evaluateGroup(group: ConditionGroup, options: EvaluateOptions): boolean {
33
+ const { children, connectors } = group
34
+ if (children.length === 0) return true
35
+
36
+ let acc = evaluateNode(children[0]!, options)
37
+ for (let i = 1; i < children.length; i++) {
38
+ const connector: ConditionConnector = connectors[i - 1] ?? 'and'
39
+ const current = evaluateNode(children[i]!, options)
40
+ acc = connector === 'and' ? acc && current : acc || current
41
+ }
42
+ return acc
11
43
  }
12
44
 
13
- function getByPath(obj: Record<string, unknown>, path: string): unknown {
14
- return path.split('.').reduce((acc: any, key) => (acc != null ? acc[key] : undefined), obj)
45
+ function evaluateNode(node: ConditionNode, options: EvaluateOptions): boolean {
46
+ return node.type === 'group' ? evaluateGroup(node, options) : evaluateLeaf(node, options)
15
47
  }
16
48
 
17
- function evaluateItem(item: ConditionItem, data: Record<string, unknown>): boolean {
18
- const resolved = resolveValue(item, data)
19
- const compareValue = resolveCompareValue(item, data)
49
+ function evaluateLeaf(leaf: ConditionLeaf, options: EvaluateOptions): boolean {
50
+ if (!leaf.field || !leaf.operator) return false
51
+ const field = options.fields?.find((f) => f.value === leaf.field)
52
+ const left = readField(leaf.field, options.data)
53
+ const op = leaf.operator
54
+
55
+ if (op === 'empty') return isEmpty(left)
56
+ if (op === 'not_empty') return !isEmpty(left)
57
+ if (op === 'is_true') return toBool(left) === true
58
+ if (op === 'is_false') return toBool(left) === false
20
59
 
21
- switch (item.operator) {
22
- case 'empty':
23
- return resolved === '' || resolved === 'undefined' || resolved === 'null'
24
- case 'not_empty':
25
- return resolved !== '' && resolved !== 'undefined' && resolved !== 'null'
26
- case 'is_true':
27
- return resolved === 'true' || resolved === '1'
28
- case 'is_false':
29
- return resolved === 'false' || resolved === '0' || resolved === ''
60
+ // 其余 operator 需要一个右侧比较值
61
+ if (!isValueProvided(leaf.value)) return false
62
+ const right = resolveValue(leaf.value, options.data)
63
+
64
+ const fieldType = field?.type ?? 'string'
65
+
66
+ switch (op) {
30
67
  case 'eq':
31
- return resolved === compareValue
68
+ return compareEq(left, right, fieldType)
32
69
  case 'ne':
33
- return resolved !== compareValue
70
+ return !compareEq(left, right, fieldType)
34
71
  case 'contains':
35
- return resolved.includes(compareValue)
72
+ return toStr(left).includes(toStr(right))
36
73
  case 'not_contains':
37
- return !resolved.includes(compareValue)
74
+ return !toStr(left).includes(toStr(right))
38
75
  case 'gt':
39
- return Number(resolved) > Number(compareValue)
76
+ return toNumber(left) > toNumber(right)
40
77
  case 'lt':
41
- return Number(resolved) < Number(compareValue)
78
+ return toNumber(left) < toNumber(right)
42
79
  case 'gte':
43
- return Number(resolved) >= Number(compareValue)
80
+ return toNumber(left) >= toNumber(right)
44
81
  case 'lte':
45
- return Number(resolved) <= Number(compareValue)
82
+ return toNumber(left) <= toNumber(right)
46
83
  case 'before':
47
- return new Date(resolved).getTime() < new Date(compareValue).getTime()
84
+ return toTime(left) < toTime(right)
48
85
  case 'after':
49
- return new Date(resolved).getTime() > new Date(compareValue).getTime()
86
+ return toTime(left) > toTime(right)
50
87
  case 'in': {
51
- const vals = compareValue.split(',').map((s) => s.trim())
52
- return vals.includes(resolved)
88
+ // 右侧可能是数组、逗号分隔字符串或单值
89
+ const list = Array.isArray(right)
90
+ ? right.map(toStr)
91
+ : toStr(right)
92
+ .split(',')
93
+ .map((s) => s.trim())
94
+ .filter(Boolean)
95
+ return list.includes(toStr(left))
53
96
  }
54
97
  default:
55
98
  return false
56
99
  }
57
100
  }
58
101
 
59
- function evaluateGroup(group: ConditionGroup, data: Record<string, unknown>): boolean {
60
- const conditionsResult = group.conditions.map((item) => {
61
- const result = evaluateItem(item, data)
62
- item._result = result
63
- return result
64
- })
102
+ function readField(path: string, data?: Record<string, unknown>): unknown {
103
+ if (!data) return undefined
104
+ return o(data).get(path)
105
+ }
65
106
 
66
- const groupsResult = group.groups.map((sub) => {
67
- const result = evaluateGroup(sub, data)
68
- sub._result = result
69
- return result
70
- })
107
+ function resolveValue(value: ConditionValue, data?: Record<string, unknown>): unknown {
108
+ if (value.kind === 'constant') return value.value
109
+ return readField(value.name, data)
110
+ }
71
111
 
72
- const allResults = [...conditionsResult, ...groupsResult]
73
- if (allResults.length === 0) return false
112
+ function isValueProvided(value: ConditionValue): boolean {
113
+ if (value.kind === 'constant') return value.value !== ''
114
+ return value.name !== ''
115
+ }
74
116
 
75
- return group.logic === 'and' ? allResults.every(Boolean) : allResults.some(Boolean)
117
+ function isEmpty(v: unknown): boolean {
118
+ if (v == null) return true
119
+ if (typeof v === 'string') return v.trim() === ''
120
+ if (Array.isArray(v)) return v.length === 0
121
+ return false
76
122
  }
77
123
 
78
- export function evaluate(
79
- expression: ConditionExpression,
80
- data: Record<string, unknown>
81
- ): ConditionExpression {
82
- const cloned = JSON.parse(JSON.stringify(expression)) as ConditionExpression
83
- evaluateGroup(cloned, data)
84
- return cloned
124
+ function toBool(v: unknown): boolean {
125
+ if (typeof v === 'boolean') return v
126
+ if (typeof v === 'number') return v !== 0
127
+ if (typeof v === 'string') {
128
+ const s = v.trim().toLowerCase()
129
+ if (s === 'true' || s === '1') return true
130
+ if (s === 'false' || s === '0' || s === '') return false
131
+ }
132
+ return Boolean(v)
85
133
  }
86
134
 
135
+ function toNumber(v: unknown): number {
136
+ if (typeof v === 'number') return v
137
+ if (typeof v === 'boolean') return v ? 1 : 0
138
+ if (typeof v === 'string') {
139
+ const n = Number(v)
140
+ return Number.isNaN(n) ? Number.NaN : n
141
+ }
142
+ return Number.NaN
143
+ }
144
+
145
+ function toTime(v: unknown): number {
146
+ if (v instanceof Date) return v.getTime()
147
+ if (typeof v === 'number') return v
148
+ if (typeof v === 'string' && v) return new Date(v).getTime()
149
+ return Number.NaN
150
+ }
151
+
152
+ function compareEq(left: unknown, right: unknown, type: ConditionField['type']): boolean {
153
+ if (type === 'number') return toNumber(left) === toNumber(right)
154
+ if (type === 'boolean') return toBool(left) === toBool(right)
155
+ if (type === 'date') return toTime(left) === toTime(right)
156
+ return toStr(left) === toStr(right)
157
+ }
158
+
159
+ function toStr(v: unknown): string {
160
+ if (v == null) return ''
161
+ if (typeof v === 'string') return v
162
+ if (typeof v === 'number' || typeof v === 'boolean' || typeof v === 'bigint') return String(v)
163
+ if (v instanceof Date) return v.toISOString()
164
+ try {
165
+ return JSON.stringify(v)
166
+ } catch {
167
+ return ''
168
+ }
169
+ }
170
+
171
+ // ── 工厂 ─────────────────────────────────────────
172
+
87
173
  export function createEmptyGroup(): ConditionGroup {
88
- return { logic: 'and', conditions: [], groups: [] }
174
+ return { type: 'group', children: [], connectors: [] }
89
175
  }
90
176
 
91
- export function createEmptyItem(): ConditionItem {
92
- return { field: '', operator: 'eq', value: { kind: 'constant', value: '' } }
177
+ export function createEmptyLeaf(): ConditionLeaf {
178
+ return { type: 'condition', field: '', operator: 'eq', value: { kind: 'constant', value: '' } }
93
179
  }
@@ -1 +1,7 @@
1
1
  export { default as UConditionEditor } from './condition-editor.vue'
2
+ export {
3
+ createEmptyGroup,
4
+ createEmptyLeaf,
5
+ evaluate as evaluateConditionExpression
6
+ } from './core/evaluator'
7
+ export type { EvaluateOptions as ConditionEvaluateOptions } from './core/evaluator'