@veltra/desktop 1.1.4 → 1.1.5

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 (145) hide show
  1. package/dist/components/action/action-group.js +3 -110
  2. package/dist/components/action/action-group.js.map +1 -1
  3. package/dist/components/action/action-group.vue.d.ts +4 -13
  4. package/dist/components/action/action-group.vue_vue_type_script_setup_true_lang.js +105 -0
  5. package/dist/components/action/action-group.vue_vue_type_script_setup_true_lang.js.map +1 -0
  6. package/dist/components/action/action.js +5 -1
  7. package/dist/components/action/action.js.map +1 -1
  8. package/dist/components/action/action.vue.d.ts +1 -1
  9. package/dist/components/action/di.js.map +1 -1
  10. package/dist/components/action/style2.css +17 -14
  11. package/dist/components/auto-complete/auto-complete.js +1 -1
  12. package/dist/components/badge/badge.js +1 -1
  13. package/dist/components/batch-edit/batch-edit-form.js +94 -101
  14. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  15. package/dist/components/batch-edit/batch-edit-list.js +52 -68
  16. package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
  17. package/dist/components/batch-edit/style2.css +13 -89
  18. package/dist/components/breadcrumb/breadcrumb.js +1 -1
  19. package/dist/components/button/button.js +1 -1
  20. package/dist/components/card/card.js +1 -1
  21. package/dist/components/cascade/cascade-panel-item.js +4 -4
  22. package/dist/components/cascade/cascade.js +2 -2
  23. package/dist/components/cascade/cascade.vue.d.ts +2 -2
  24. package/dist/components/checkbox/checkbox-button.js +1 -1
  25. package/dist/components/checkbox/checkbox-button.vue.d.ts +1 -1
  26. package/dist/components/checkbox/checkbox.js +1 -1
  27. package/dist/components/checkbox-group/checkbox-group.js +1 -1
  28. package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
  29. package/dist/components/code-editor/code-editor.js +1 -1
  30. package/dist/components/collapse/collapse.js +1 -1
  31. package/dist/components/condition-editor/components/condition-group.js +152 -0
  32. package/dist/components/condition-editor/components/condition-group.js.map +1 -0
  33. package/dist/components/condition-editor/components/condition-row.js +217 -0
  34. package/dist/components/condition-editor/components/condition-row.js.map +1 -0
  35. package/dist/components/condition-editor/condition-editor.js +134 -5
  36. package/dist/components/condition-editor/condition-editor.js.map +1 -1
  37. package/dist/components/condition-editor/condition-editor.vue.d.ts +10 -2
  38. package/dist/components/condition-editor/core/evaluator.js +75 -0
  39. package/dist/components/condition-editor/core/evaluator.js.map +1 -0
  40. package/dist/components/condition-editor/core/operators.js +123 -0
  41. package/dist/components/condition-editor/core/operators.js.map +1 -0
  42. package/dist/components/condition-editor/style.js +7 -0
  43. package/dist/components/condition-editor/style2.css +186 -0
  44. package/dist/components/context-menu/context-menu.js +1 -1
  45. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  46. package/dist/components/date-picker/date-picker.js +2 -2
  47. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  48. package/dist/components/date-range-picker/date-range-picker.js +2 -2
  49. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  50. package/dist/components/dialog/dialog.js +7 -7
  51. package/dist/components/dialog/style2.css +8 -0
  52. package/dist/components/dialog/use-maximum.js +1 -1
  53. package/dist/components/drawer/style2.css +8 -0
  54. package/dist/components/dropdown/dropdown.js +1 -1
  55. package/dist/components/expression-editor/components/variable-picker.js +8 -8
  56. package/dist/components/expression-editor/expression-editor.js +1 -1
  57. package/dist/components/file-viewer/file-viewer.js +13 -13
  58. package/dist/components/form-item/form-item.js.map +1 -1
  59. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js +6 -6
  60. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js.map +1 -1
  61. package/dist/components/grid-input/grid-input.js +1 -1
  62. package/dist/components/group-input/group-input.js +1 -1
  63. package/dist/components/index.js +154 -154
  64. package/dist/components/input/input.vue_vue_type_script_setup_true_lang.js +1 -1
  65. package/dist/components/layout/layout-resizer.js +1 -1
  66. package/dist/components/layout/layout.js +1 -1
  67. package/dist/components/list/list.js +1 -1
  68. package/dist/components/loading/loading.js +1 -1
  69. package/dist/components/menu/menu-sub-collapsed.js +4 -4
  70. package/dist/components/menu/menu.js +1 -1
  71. package/dist/components/message/message.js +1 -1
  72. package/dist/components/message-confirm/message-confirm.js +1 -1
  73. package/dist/components/multi-select/multi-select.js +15 -15
  74. package/dist/components/multi-select/multi-select.vue.d.ts +3 -3
  75. package/dist/components/multi-tree-select/multi-tree-select.js +8 -8
  76. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +3 -3
  77. package/dist/components/notification/notification.js +1 -1
  78. package/dist/components/number/number.vue.d.ts +1 -1
  79. package/dist/components/number-input/number-input.js +2 -2
  80. package/dist/components/number-range-input/number-range-input.js +1 -1
  81. package/dist/components/paginator/paginator.js +6 -6
  82. package/dist/components/paginator/style.js +1 -1
  83. package/dist/components/palette/palette.js +1 -1
  84. package/dist/components/password-input/password-input.js +2 -2
  85. package/dist/components/pop-confirm/pop-confirm.js +1 -1
  86. package/dist/components/progress-nodes/progress-nodes.js +1 -1
  87. package/dist/components/radio/radio.js +1 -1
  88. package/dist/components/radio-group/radio-group.js +1 -1
  89. package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
  90. package/dist/components/rich-text-editor/rich-text-editor.js +1 -1
  91. package/dist/components/scroll/scroll-bar.js +1 -1
  92. package/dist/components/scroll/scroll.js +1 -1
  93. package/dist/components/select/select.js +10 -10
  94. package/dist/components/select/select.vue.d.ts +2 -2
  95. package/dist/components/slider/slider.js +1 -1
  96. package/dist/components/steps/steps.js +2 -2
  97. package/dist/components/switch/switch.js +1 -1
  98. package/dist/components/table/style2.css +4 -1
  99. package/dist/components/table/table.js +2 -2
  100. package/dist/components/table/table.js.map +1 -1
  101. package/dist/components/table/use-columns.js +1 -1
  102. package/dist/components/table/use-fixed-columns.js +8 -31
  103. package/dist/components/table/use-fixed-columns.js.map +1 -1
  104. package/dist/components/table-editor/table-editor.js +1 -1
  105. package/dist/components/tabs/tabs-horizontal.js +1 -1
  106. package/dist/components/tabs/tabs-vertical.js +1 -1
  107. package/dist/components/tabs/tabs.js +1 -1
  108. package/dist/components/tag/tag.js +1 -1
  109. package/dist/components/textarea/textarea.js +1 -1
  110. package/dist/components/theme/style.js +1 -1
  111. package/dist/components/theme/theme.js +11 -11
  112. package/dist/components/tip/tip.js +1 -1
  113. package/dist/components/tree/tree-node.js +1 -1
  114. package/dist/components/tree/tree.js +5 -5
  115. package/dist/components/tree-select/tree-select.js +6 -6
  116. package/dist/components/tree-select/tree-select.vue.d.ts +3 -3
  117. package/dist/index.d.ts +5 -5
  118. package/dist/index.js +83 -83
  119. package/dist/style.js +2 -2
  120. package/dist/types/action.d.ts +5 -11
  121. package/dist/types/condition-editor.d.ts +41 -8
  122. package/dist/types/index.d.ts +4 -4
  123. package/package.json +11 -11
  124. package/src/components/action/action-group.vue +68 -82
  125. package/src/components/action/action.vue +9 -1
  126. package/src/components/action/di.ts +2 -1
  127. package/src/components/action/style.scss +22 -27
  128. package/src/components/batch-edit/batch-edit-form.vue +65 -70
  129. package/src/components/batch-edit/batch-edit-list.vue +35 -41
  130. package/src/components/batch-edit/style.scss +19 -118
  131. package/src/components/condition-editor/components/condition-group.vue +131 -0
  132. package/src/components/condition-editor/components/condition-row.vue +208 -0
  133. package/src/components/condition-editor/condition-editor.vue +145 -6
  134. package/src/components/condition-editor/core/evaluator.ts +93 -0
  135. package/src/components/condition-editor/core/operators.ts +57 -0
  136. package/src/components/condition-editor/style.scss +239 -1
  137. package/src/components/condition-editor/style.ts +7 -0
  138. package/src/components/dialog/style.scss +13 -0
  139. package/src/components/drawer/style.scss +13 -0
  140. package/src/components/form-item/form-item.vue +1 -1
  141. package/src/components/table/style.scss +9 -1
  142. package/src/components/table/table.vue +1 -3
  143. package/src/components/table/use-fixed-columns.ts +5 -49
  144. package/src/types/action.ts +6 -11
  145. package/src/types/condition-editor.ts +39 -7
@@ -1,17 +1,156 @@
1
1
  <template>
2
- <div :class="cls.b"></div>
2
+ <div :class="className" @keydown="onKeydown">
3
+ <ConditionGroup
4
+ :group="rootGroup"
5
+ :fields="props.fields"
6
+ :size="size"
7
+ :disabled="disabled"
8
+ :readonly="readonly"
9
+ @update:group="onGroupUpdate"
10
+ @mention="onMention"
11
+ />
12
+
13
+ <VariablePicker
14
+ ref="pickerRef"
15
+ :visible="pickerVisible"
16
+ :trigger-dom="pickerTriggerDom"
17
+ :variables="props.variables"
18
+ :filter="pickerFilter"
19
+ :selectable-levels="'leaf'"
20
+ @select="onPickerSelect"
21
+ @dismiss="onPickerDismiss"
22
+ @update:visible="onPickerVisibleChange"
23
+ />
24
+ </div>
3
25
  </template>
4
26
 
5
27
  <script lang="ts" setup>
6
- import { bem } from '@veltra/utils'
28
+ import { useFormFallbackProps } from '@veltra/compositions'
29
+ import { bem, injectFormContext } from '@veltra/utils'
30
+ import { computed, nextTick, shallowRef, useTemplateRef, watch } from 'vue'
31
+
32
+ import type { ConditionEditorProps, ConditionExpression } from '../../types'
33
+ import VariablePicker from '../expression-editor/components/variable-picker.vue'
34
+ import ConditionGroup from './components/condition-group.vue'
35
+ import type { MentionPayload } from './components/condition-row.vue'
36
+ import { createEmptyGroup, evaluate } from './core/evaluator'
7
37
 
8
- import type { ConditionEditorProps } from '../../types'
38
+ defineOptions({ name: 'ConditionEditor' })
9
39
 
10
- defineOptions({
11
- name: 'ConditionEditor'
40
+ const props = withDefaults(defineProps<ConditionEditorProps>(), {
41
+ fields: () => []
12
42
  })
13
43
 
14
- defineProps<ConditionEditorProps>()
44
+ const emit = defineEmits<{
45
+ (e: 'update:modelValue', value: ConditionExpression): void
46
+ (e: 'evaluate', results: ConditionExpression): void
47
+ }>()
15
48
 
16
49
  const cls = bem('condition-editor')
50
+ const { formProps } = injectFormContext()
51
+ const { size, disabled, readonly } = useFormFallbackProps([formProps ?? {}, props])
52
+
53
+ const className = computed(() => [
54
+ cls.b,
55
+ cls.m(size.value),
56
+ bem.is('disabled', disabled.value),
57
+ bem.is('readonly', readonly.value)
58
+ ])
59
+
60
+ const rootGroup = shallowRef<ConditionExpression>(
61
+ props.modelValue ? JSON.parse(JSON.stringify(props.modelValue)) : createEmptyGroup()
62
+ )
63
+
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)))
76
+ }
77
+
78
+ function emitUpdate() {
79
+ emit('update:modelValue', serialize())
80
+ }
81
+
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
+ }
89
+
90
+ // ── VariablePicker ──
91
+
92
+ const pickerRef = useTemplateRef<{ handleKeydown: (e: KeyboardEvent) => boolean }>('pickerRef')
93
+ const pickerVisible = shallowRef(false)
94
+ const pickerTriggerDom = shallowRef<HTMLElement | undefined>(undefined)
95
+ const pickerFilter = shallowRef('')
96
+ const mentionTarget = shallowRef<MentionPayload | null>(null)
97
+
98
+ function onMention(payload: MentionPayload) {
99
+ if (disabled.value || readonly.value) return
100
+ mentionTarget.value = payload
101
+ pickerTriggerDom.value = payload.triggerDom
102
+ pickerFilter.value = ''
103
+ pickerVisible.value = true
104
+ }
105
+
106
+ function onPickerSelect(item: { label: string; value: string }) {
107
+ mentionTarget.value?.setValue({ kind: 'variable', name: item.value })
108
+ pickerVisible.value = false
109
+ mentionTarget.value = null
110
+ }
111
+
112
+ function onPickerDismiss() {
113
+ pickerVisible.value = false
114
+ mentionTarget.value = null
115
+ }
116
+
117
+ function onPickerVisibleChange(v: boolean) {
118
+ if (!v) {
119
+ pickerVisible.value = false
120
+ mentionTarget.value = null
121
+ }
122
+ }
123
+
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
+ function onKeydown(e: KeyboardEvent) {
147
+ if (disabled.value || readonly.value) return
148
+ if (pickerVisible.value) {
149
+ const handled = pickerRef.value?.handleKeydown(e)
150
+ if (handled) {
151
+ e.preventDefault()
152
+ return
153
+ }
154
+ }
155
+ }
17
156
  </script>
@@ -0,0 +1,93 @@
1
+ import type { ConditionExpression, ConditionGroup, ConditionItem } from '../../../types'
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) ?? '')
6
+ }
7
+
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) ?? '')
11
+ }
12
+
13
+ function getByPath(obj: Record<string, unknown>, path: string): unknown {
14
+ return path.split('.').reduce((acc: any, key) => (acc != null ? acc[key] : undefined), obj)
15
+ }
16
+
17
+ function evaluateItem(item: ConditionItem, data: Record<string, unknown>): boolean {
18
+ const resolved = resolveValue(item, data)
19
+ const compareValue = resolveCompareValue(item, data)
20
+
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 === ''
30
+ case 'eq':
31
+ return resolved === compareValue
32
+ case 'ne':
33
+ return resolved !== compareValue
34
+ case 'contains':
35
+ return resolved.includes(compareValue)
36
+ case 'not_contains':
37
+ return !resolved.includes(compareValue)
38
+ case 'gt':
39
+ return Number(resolved) > Number(compareValue)
40
+ case 'lt':
41
+ return Number(resolved) < Number(compareValue)
42
+ case 'gte':
43
+ return Number(resolved) >= Number(compareValue)
44
+ case 'lte':
45
+ return Number(resolved) <= Number(compareValue)
46
+ case 'before':
47
+ return new Date(resolved).getTime() < new Date(compareValue).getTime()
48
+ case 'after':
49
+ return new Date(resolved).getTime() > new Date(compareValue).getTime()
50
+ case 'in': {
51
+ const vals = compareValue.split(',').map((s) => s.trim())
52
+ return vals.includes(resolved)
53
+ }
54
+ default:
55
+ return false
56
+ }
57
+ }
58
+
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
+ })
65
+
66
+ const groupsResult = group.groups.map((sub) => {
67
+ const result = evaluateGroup(sub, data)
68
+ sub._result = result
69
+ return result
70
+ })
71
+
72
+ const allResults = [...conditionsResult, ...groupsResult]
73
+ if (allResults.length === 0) return false
74
+
75
+ return group.logic === 'and' ? allResults.every(Boolean) : allResults.some(Boolean)
76
+ }
77
+
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
85
+ }
86
+
87
+ export function createEmptyGroup(): ConditionGroup {
88
+ return { logic: 'and', conditions: [], groups: [] }
89
+ }
90
+
91
+ export function createEmptyItem(): ConditionItem {
92
+ return { field: '', operator: 'eq', value: { kind: 'constant', value: '' } }
93
+ }
@@ -0,0 +1,57 @@
1
+ export interface OperatorDef {
2
+ label: string
3
+ value: string
4
+ needValue: boolean
5
+ }
6
+
7
+ const STRING_OPERATORS: OperatorDef[] = [
8
+ { label: '等于', value: 'eq', needValue: true },
9
+ { label: '不等于', value: 'ne', needValue: true },
10
+ { label: '包含', value: 'contains', needValue: true },
11
+ { label: '不包含', value: 'not_contains', needValue: true },
12
+ { label: '为空', value: 'empty', needValue: false },
13
+ { label: '不为空', value: 'not_empty', needValue: false }
14
+ ]
15
+
16
+ const NUMBER_OPERATORS: OperatorDef[] = [
17
+ { label: '等于', value: 'eq', needValue: true },
18
+ { label: '不等于', value: 'ne', needValue: true },
19
+ { label: '大于', value: 'gt', needValue: true },
20
+ { label: '小于', value: 'lt', needValue: true },
21
+ { label: '大于等于', value: 'gte', needValue: true },
22
+ { label: '小于等于', value: 'lte', needValue: true }
23
+ ]
24
+
25
+ const BOOLEAN_OPERATORS: OperatorDef[] = [
26
+ { label: '是', value: 'is_true', needValue: false },
27
+ { label: '否', value: 'is_false', needValue: false }
28
+ ]
29
+
30
+ const DATE_OPERATORS: OperatorDef[] = [
31
+ { label: '等于', value: 'eq', needValue: true },
32
+ { label: '不等于', value: 'ne', needValue: true },
33
+ { label: '早于', value: 'before', needValue: true },
34
+ { label: '晚于', value: 'after', needValue: true }
35
+ ]
36
+
37
+ const ENUM_OPERATORS: OperatorDef[] = [
38
+ { label: '等于', value: 'eq', needValue: true },
39
+ { label: '不等于', value: 'ne', needValue: true },
40
+ { label: '包含于', value: 'in', needValue: true }
41
+ ]
42
+
43
+ const OPERATOR_MAP: Record<string, OperatorDef[]> = {
44
+ string: STRING_OPERATORS,
45
+ number: NUMBER_OPERATORS,
46
+ boolean: BOOLEAN_OPERATORS,
47
+ date: DATE_OPERATORS,
48
+ enum: ENUM_OPERATORS
49
+ }
50
+
51
+ export function getOperatorsByFieldType(type: string): OperatorDef[] {
52
+ return OPERATOR_MAP[type] ?? STRING_OPERATORS
53
+ }
54
+
55
+ export function getOperatorDef(type: string, operator: string): OperatorDef | undefined {
56
+ return getOperatorsByFieldType(type).find((op) => op.value === operator)
57
+ }
@@ -2,8 +2,246 @@
2
2
  @use 'pkg:@veltra/styles/functions' as fn;
3
3
  @use 'pkg:@veltra/styles/vars';
4
4
 
5
- // 方便拼接
6
5
  $root-name: condition-editor;
7
6
 
8
7
  @include m.b($root-name) {
8
+ // ─── 条件分组 ────────────────────────────────────
9
+ @include m.e(group) {
10
+ // 外层不需额外边框
11
+ }
12
+
13
+ @include m.e(group-header) {
14
+ display: flex;
15
+ align-items: center;
16
+ gap: 6px;
17
+ margin-bottom: 4px;
18
+ }
19
+
20
+ @include m.e(group-body) {
21
+ border-left: 2px solid fn.use-var(border, muted-color);
22
+ margin-left: 8px;
23
+ padding-left: 10px;
24
+ }
25
+
26
+ @include m.e(group-actions) {
27
+ display: flex;
28
+ gap: 10px;
29
+ padding: 2px 0;
30
+ }
31
+
32
+ @include m.e(group-result) {
33
+ font-size: 11px;
34
+ font-weight: 600;
35
+
36
+ &--pass {
37
+ color: fn.use-var(color, success);
38
+ }
39
+
40
+ &--fail {
41
+ color: fn.use-var(color, danger);
42
+ }
43
+ }
44
+
45
+ // ─── AND/OR 标签 ──────────────────────────────────
46
+ @include m.e(logic-tag) {
47
+ display: inline-block;
48
+ padding: 1px 8px;
49
+ border-radius: fn.use-var(radius, small);
50
+ font-size: 11px;
51
+ font-weight: 600;
52
+ cursor: pointer;
53
+ user-select: none;
54
+ transition: background-color 0.12s ease;
55
+
56
+ &--and {
57
+ background-color: fn.use-var(color, primary, light-9);
58
+ color: fn.use-var(color, primary);
59
+
60
+ &:hover {
61
+ background-color: fn.use-var(color, primary, light-8);
62
+ }
63
+ }
64
+
65
+ &--or {
66
+ background-color: fn.use-var(color, warning, light-9);
67
+ color: fn.use-var(color, warning);
68
+
69
+ &:hover {
70
+ background-color: fn.use-var(color, warning, light-8);
71
+ }
72
+ }
73
+ }
74
+
75
+ // ─── 条件行 ──────────────────────────────────────
76
+ @include m.e(row) {
77
+ display: flex;
78
+ gap: 4px;
79
+ align-items: center;
80
+ margin-bottom: 3px;
81
+ }
82
+
83
+ @include m.e(field) {
84
+ width: 90px;
85
+ flex-shrink: 0;
86
+ }
87
+
88
+ @include m.e(operator) {
89
+ width: 80px;
90
+ flex-shrink: 0;
91
+ }
92
+
93
+ @include m.e(value) {
94
+ flex: 1;
95
+ min-width: 0;
96
+ display: flex;
97
+ align-items: center;
98
+ }
99
+
100
+ @include m.e(value-input) {
101
+ width: 100%;
102
+ height: 28px;
103
+ padding: 0 8px;
104
+ border: 1px solid fn.use-var(border, muted-color);
105
+ border-radius: fn.use-var(radius, small);
106
+ font-size: 12px;
107
+ color: fn.use-var(text-color, main);
108
+ background-color: fn.use-var(bg-color, top);
109
+ outline: none;
110
+ transition: border-color 0.12s ease;
111
+
112
+ &::placeholder {
113
+ color: fn.use-var(text-color, placeholder);
114
+ }
115
+
116
+ &:focus {
117
+ border-color: fn.use-var(color, primary);
118
+ }
119
+
120
+ &:disabled {
121
+ background-color: fn.use-var(color, disabled);
122
+ cursor: not-allowed;
123
+ }
124
+ }
125
+
126
+ @include m.e(value-empty) {
127
+ font-size: 12px;
128
+ color: fn.use-var(text-color, assist);
129
+ }
130
+
131
+ // ─── 变量 chip ──────────────────────────────────
132
+ @include m.e(value-chip) {
133
+ display: inline-flex;
134
+ align-items: center;
135
+ gap: 4px;
136
+ padding: 0 8px;
137
+ height: 22px;
138
+ font-size: 12px;
139
+ color: fn.use-var(color, primary);
140
+ background-color: fn.use-var(color, primary, light-9);
141
+ border-radius: 999px;
142
+ cursor: pointer;
143
+ user-select: none;
144
+ transition:
145
+ background-color 0.12s ease,
146
+ box-shadow 0.12s ease;
147
+
148
+ &:hover {
149
+ background-color: fn.use-var(color, primary, light-8);
150
+
151
+ .#{vars.$namespace}#{$root-name}__value-chip-close {
152
+ opacity: 1;
153
+ max-width: 14px;
154
+ margin-left: 2px;
155
+ }
156
+ }
157
+ }
158
+
159
+ @include m.e(value-chip-label) {
160
+ pointer-events: none;
161
+ }
162
+
163
+ @include m.e(value-chip-close) {
164
+ display: inline-flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ width: 14px;
168
+ max-width: 0;
169
+ margin-left: 0;
170
+ overflow: hidden;
171
+ font-size: 14px;
172
+ line-height: 1;
173
+ color: fn.use-var(color, primary);
174
+ opacity: 0;
175
+ transition:
176
+ opacity 0.12s ease,
177
+ max-width 0.12s ease,
178
+ margin-left 0.12s ease;
179
+
180
+ &:hover {
181
+ transform: scale(1.1);
182
+ }
183
+ }
184
+
185
+ // ─── 删除按钮 ────────────────────────────────────
186
+ @include m.e(row-delete) {
187
+ flex-shrink: 0;
188
+ width: 16px;
189
+ text-align: center;
190
+ font-size: 15px;
191
+ color: fn.use-var(text-color, assist);
192
+ cursor: pointer;
193
+ transition: color 0.12s ease;
194
+
195
+ &:hover {
196
+ color: fn.use-var(color, danger);
197
+ }
198
+ }
199
+
200
+ // ─── 结果指示 ────────────────────────────────────
201
+ @include m.e(result) {
202
+ flex-shrink: 0;
203
+ width: 16px;
204
+ text-align: center;
205
+ font-size: 11px;
206
+ font-weight: 600;
207
+
208
+ &--pass {
209
+ color: fn.use-var(color, success);
210
+ }
211
+
212
+ &--fail {
213
+ color: fn.use-var(color, danger);
214
+ }
215
+
216
+ &--none {
217
+ color: fn.use-var(text-color, assist);
218
+ }
219
+ }
220
+
221
+ // ─── 添加按钮 ────────────────────────────────────
222
+ @include m.e(add-btn) {
223
+ font-size: 11px;
224
+ color: fn.use-var(color, primary);
225
+ cursor: pointer;
226
+ transition: color 0.12s ease;
227
+
228
+ &:hover {
229
+ color: fn.use-var(color, primary, dark-2);
230
+ }
231
+ }
232
+
233
+ // ─── 状态 ──────────────────────────────────────
234
+ @include m.is(disabled) {
235
+ cursor: not-allowed;
236
+
237
+ @include m.bem($root-name, logic-tag) {
238
+ cursor: not-allowed;
239
+ opacity: 0.6;
240
+ }
241
+
242
+ @include m.bem($root-name, add-btn) {
243
+ cursor: not-allowed;
244
+ opacity: 0.6;
245
+ }
246
+ }
9
247
  }
@@ -1 +1,8 @@
1
+ import '../select/style'
2
+ import '../input/style'
3
+ import '../icon/style'
4
+ import '../scroll/style'
5
+ import '../empty/style'
6
+ import '../tip/style'
7
+ import '../expression-editor/style'
1
8
  import './style.scss'
@@ -18,6 +18,19 @@ $root-name: dialog;
18
18
  background-color: rgba($color: #000000, $alpha: 0.32);
19
19
  pointer-events: auto;
20
20
  }
21
+
22
+ // 确保遮罩淡入淡出不受外部 fade 样式(如 transform)影响
23
+ &.fade-enter-active,
24
+ &.fade-leave-active {
25
+ transition: opacity 0.25s linear;
26
+ }
27
+
28
+ &.fade-enter-from,
29
+ &.fade-leave-to {
30
+ opacity: 0;
31
+ transform: none;
32
+ filter: none;
33
+ }
21
34
  }
22
35
 
23
36
  @include m.b($root-name) {
@@ -107,6 +107,19 @@ $direction-map: (
107
107
  height: 100vh;
108
108
  background-color: rgba($color: #000000, $alpha: 0.32);
109
109
  overflow: hidden;
110
+
111
+ // 确保遮罩淡入淡出不受外部 fade 样式(如 transform)影响
112
+ &.fade-enter-active,
113
+ &.fade-leave-active {
114
+ transition: opacity 0.25s linear;
115
+ }
116
+
117
+ &.fade-enter-from,
118
+ &.fade-leave-to {
119
+ opacity: 0;
120
+ transform: none;
121
+ filter: none;
122
+ }
110
123
  }
111
124
 
112
125
  // 动画
@@ -81,7 +81,7 @@ const labelStyles = computed<CSSProperties>(() => {
81
81
  /** 错误提示 */
82
82
  const errorTips = computed<string | undefined>(() => {
83
83
  if (props.field) {
84
- return formProps?.model.errors.get(props.field)?.[0]
84
+ return formProps?.model?.errors.get(props.field)?.[0]
85
85
  }
86
86
  })
87
87
 
@@ -17,8 +17,8 @@ $root-name: table;
17
17
  width: 100%;
18
18
  position: relative;
19
19
  border: 1px solid var(--u-table-border-color);
20
- border-bottom: none;
21
20
  overflow: hidden;
21
+ background-color: fn.use-var(bg-color, top);
22
22
 
23
23
  @include m.size using ($size) {
24
24
  border-radius: fn.use-var(radius, $size);
@@ -171,6 +171,14 @@ $root-name: table;
171
171
 
172
172
  @include m.e(body) {
173
173
  will-change: transform;
174
+
175
+ @include m.bem($root-name, row) {
176
+ &:last-child {
177
+ @include m.bem($root-name, cell) {
178
+ border-bottom: none;
179
+ }
180
+ }
181
+ }
174
182
  }
175
183
 
176
184
  @include m.e(empty) {
@@ -151,9 +151,7 @@ const columnConfig = useColumns({
151
151
 
152
152
  const { leafColumns } = columnConfig
153
153
 
154
- const { handleScroll, leftFixed, rightFixed } = useFixedColumns({
155
- isScrolling: isScrollingRelay
156
- })
154
+ const { handleScroll, leftFixed, rightFixed } = useFixedColumns()
157
155
 
158
156
  const {
159
157
  getColumnSlotsNode,