@veltra/desktop 1.1.4 → 1.1.6

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 (169) 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 +2 -2
  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 +105 -100
  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/batch-edit.js +0 -1
  18. package/dist/components/batch-edit/batch-edit.js.map +1 -1
  19. package/dist/components/batch-edit/style.js +1 -0
  20. package/dist/components/batch-edit/style2.css +13 -89
  21. package/dist/components/breadcrumb/breadcrumb.js +1 -1
  22. package/dist/components/button/button.js +1 -1
  23. package/dist/components/card/card.js +1 -1
  24. package/dist/components/cascade/cascade-panel-item.js +4 -4
  25. package/dist/components/cascade/cascade.js +2 -2
  26. package/dist/components/cascade/cascade.vue.d.ts +2 -2
  27. package/dist/components/checkbox/checkbox-button.js +1 -1
  28. package/dist/components/checkbox/checkbox-button.vue.d.ts +1 -1
  29. package/dist/components/checkbox/checkbox.js +1 -1
  30. package/dist/components/checkbox-group/checkbox-group.js +1 -1
  31. package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
  32. package/dist/components/code-editor/code-editor.js +1 -1
  33. package/dist/components/collapse/collapse.js +1 -1
  34. package/dist/components/condition-editor/components/condition-group.js +152 -0
  35. package/dist/components/condition-editor/components/condition-group.js.map +1 -0
  36. package/dist/components/condition-editor/components/condition-row.js +217 -0
  37. package/dist/components/condition-editor/components/condition-row.js.map +1 -0
  38. package/dist/components/condition-editor/condition-editor.js +134 -5
  39. package/dist/components/condition-editor/condition-editor.js.map +1 -1
  40. package/dist/components/condition-editor/condition-editor.vue.d.ts +10 -2
  41. package/dist/components/condition-editor/core/evaluator.js +75 -0
  42. package/dist/components/condition-editor/core/evaluator.js.map +1 -0
  43. package/dist/components/condition-editor/core/operators.js +123 -0
  44. package/dist/components/condition-editor/core/operators.js.map +1 -0
  45. package/dist/components/condition-editor/style.js +7 -0
  46. package/dist/components/condition-editor/style2.css +186 -0
  47. package/dist/components/context-menu/context-menu.js +1 -1
  48. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  49. package/dist/components/date-picker/date-picker.js +2 -2
  50. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  51. package/dist/components/date-range-picker/date-range-picker.js +2 -2
  52. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  53. package/dist/components/dialog/dialog.js +7 -7
  54. package/dist/components/dialog/style2.css +8 -0
  55. package/dist/components/dialog/use-maximum.js +1 -1
  56. package/dist/components/drawer/style2.css +8 -0
  57. package/dist/components/dropdown/dropdown.js +1 -1
  58. package/dist/components/expression-editor/components/variable-picker.js +8 -8
  59. package/dist/components/expression-editor/expression-editor.js +1 -1
  60. package/dist/components/file-viewer/file-viewer.js +13 -13
  61. package/dist/components/form-item/form-item.js.map +1 -1
  62. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js +6 -6
  63. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js.map +1 -1
  64. package/dist/components/grid-input/grid-input.js +1 -1
  65. package/dist/components/group-input/group-input.js +1 -1
  66. package/dist/components/index.d.ts +9 -8
  67. package/dist/components/index.js +130 -128
  68. package/dist/components/index.js.map +1 -1
  69. package/dist/components/input/input.vue_vue_type_script_setup_true_lang.js +1 -1
  70. package/dist/components/kbd/index.d.ts +1 -0
  71. package/dist/components/kbd/kbd.js +17 -0
  72. package/dist/components/kbd/kbd.js.map +1 -0
  73. package/dist/components/kbd/kbd.vue.d.ts +18 -0
  74. package/dist/components/kbd/style.d.ts +1 -0
  75. package/dist/components/kbd/style.js +1 -0
  76. package/dist/components/kbd/style2.css +43 -0
  77. package/dist/components/layout/layout-resizer.js +1 -1
  78. package/dist/components/layout/layout.js +1 -1
  79. package/dist/components/list/list.js +1 -1
  80. package/dist/components/loading/loading.js +1 -1
  81. package/dist/components/menu/menu-sub-collapsed.js +4 -4
  82. package/dist/components/menu/menu.js +1 -1
  83. package/dist/components/message/message.js +1 -1
  84. package/dist/components/message-confirm/message-confirm.js +1 -1
  85. package/dist/components/multi-select/multi-select.js +15 -15
  86. package/dist/components/multi-select/multi-select.vue.d.ts +3 -3
  87. package/dist/components/multi-tree-select/multi-tree-select.js +8 -8
  88. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +3 -3
  89. package/dist/components/notification/notification.js +1 -1
  90. package/dist/components/number/number.vue.d.ts +1 -1
  91. package/dist/components/number-input/number-input.js +2 -2
  92. package/dist/components/number-range-input/number-range-input.js +1 -1
  93. package/dist/components/paginator/paginator.js +6 -6
  94. package/dist/components/paginator/style.js +1 -1
  95. package/dist/components/palette/palette.js +1 -1
  96. package/dist/components/password-input/password-input.js +2 -2
  97. package/dist/components/pop-confirm/pop-confirm.js +1 -1
  98. package/dist/components/progress-nodes/progress-nodes.js +1 -1
  99. package/dist/components/radio/radio.js +1 -1
  100. package/dist/components/radio-group/radio-group.js +1 -1
  101. package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
  102. package/dist/components/rich-text-editor/rich-text-editor.js +1 -1
  103. package/dist/components/scroll/scroll-bar.js +1 -1
  104. package/dist/components/scroll/scroll.js +1 -1
  105. package/dist/components/select/select.js +10 -10
  106. package/dist/components/select/select.vue.d.ts +2 -2
  107. package/dist/components/slider/slider.js +1 -1
  108. package/dist/components/steps/steps.js +2 -2
  109. package/dist/components/switch/switch.js +1 -1
  110. package/dist/components/table/style2.css +4 -1
  111. package/dist/components/table/table.js +2 -3
  112. package/dist/components/table/table.js.map +1 -1
  113. package/dist/components/table/use-columns.js +1 -1
  114. package/dist/components/table/use-fixed-columns.js +8 -31
  115. package/dist/components/table/use-fixed-columns.js.map +1 -1
  116. package/dist/components/table-editor/table-editor.js +1 -2
  117. package/dist/components/table-editor/table-editor.js.map +1 -1
  118. package/dist/components/tabs/tabs-horizontal.js +1 -1
  119. package/dist/components/tabs/tabs-horizontal.vue.d.ts +1 -1
  120. package/dist/components/tabs/tabs-vertical.js +1 -1
  121. package/dist/components/tabs/tabs.js +1 -1
  122. package/dist/components/tabs/tabs.vue.d.ts +1 -1
  123. package/dist/components/tag/tag.js +1 -1
  124. package/dist/components/textarea/textarea.js +1 -1
  125. package/dist/components/theme/style.js +1 -1
  126. package/dist/components/theme/theme.js +11 -11
  127. package/dist/components/tip/tip.js +1 -1
  128. package/dist/components/tree/tree-node.js +1 -1
  129. package/dist/components/tree/tree.js +5 -5
  130. package/dist/components/tree-select/tree-select.js +6 -6
  131. package/dist/components/tree-select/tree-select.vue.d.ts +3 -3
  132. package/dist/index.d.ts +48 -47
  133. package/dist/index.js +46 -45
  134. package/dist/style.js +3 -2
  135. package/dist/types/action.d.ts +5 -11
  136. package/dist/types/condition-editor.d.ts +41 -8
  137. package/dist/types/index.d.ts +4 -4
  138. package/dist/types/table.d.ts +0 -2
  139. package/package.json +11 -11
  140. package/src/components/action/action-group.vue +68 -82
  141. package/src/components/action/action.vue +9 -1
  142. package/src/components/action/di.ts +2 -1
  143. package/src/components/action/style.scss +22 -27
  144. package/src/components/batch-edit/batch-edit-form.vue +73 -70
  145. package/src/components/batch-edit/batch-edit-list.vue +35 -41
  146. package/src/components/batch-edit/style.scss +19 -118
  147. package/src/components/batch-edit/style.ts +1 -0
  148. package/src/components/condition-editor/components/condition-group.vue +131 -0
  149. package/src/components/condition-editor/components/condition-row.vue +208 -0
  150. package/src/components/condition-editor/condition-editor.vue +145 -6
  151. package/src/components/condition-editor/core/evaluator.ts +93 -0
  152. package/src/components/condition-editor/core/operators.ts +57 -0
  153. package/src/components/condition-editor/style.scss +239 -1
  154. package/src/components/condition-editor/style.ts +7 -0
  155. package/src/components/dialog/style.scss +13 -0
  156. package/src/components/drawer/style.scss +13 -0
  157. package/src/components/form-item/form-item.vue +1 -1
  158. package/src/components/index.ts +2 -0
  159. package/src/components/kbd/index.ts +1 -0
  160. package/src/components/kbd/kbd.vue +13 -0
  161. package/src/components/kbd/style.scss +37 -0
  162. package/src/components/kbd/style.ts +1 -0
  163. package/src/components/table/style.scss +9 -1
  164. package/src/components/table/table.vue +1 -3
  165. package/src/components/table/use-fixed-columns.ts +5 -49
  166. package/src/style.ts +1 -0
  167. package/src/types/action.ts +6 -11
  168. package/src/types/condition-editor.ts +39 -7
  169. package/src/types/table.ts +0 -2
@@ -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
 
@@ -130,6 +130,8 @@ export * from './breadcrumb'
130
130
 
131
131
  export * from './icon'
132
132
 
133
+ export * from './kbd'
134
+
133
135
  export * from './pop-confirm'
134
136
 
135
137
  export * from './paginator'
@@ -0,0 +1 @@
1
+ export { default as UKbd } from './kbd.vue'
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <kbd :class="cls.b">
3
+ <slot />
4
+ </kbd>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import { bem } from '@veltra/utils'
9
+
10
+ defineOptions({ name: 'Kbd' })
11
+
12
+ const cls = bem('kbd')
13
+ </script>
@@ -0,0 +1,37 @@
1
+ @use 'pkg:@veltra/styles/mixins' as m;
2
+ @use 'pkg:@veltra/styles/functions' as fn;
3
+
4
+ @include m.b(kbd) {
5
+ display: inline-flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ flex-shrink: 0;
9
+ font-family: fn.use-var(font-family);
10
+ font-weight: normal;
11
+ vertical-align: text-top;
12
+ white-space: nowrap;
13
+ user-select: none;
14
+
15
+ position: relative;
16
+ top: -0.03em;
17
+
18
+ font-size: 0.75em;
19
+ min-width: 1.75em;
20
+ line-height: 1.7em;
21
+ padding: 0 0.5em 0.05em;
22
+ border-radius: calc(fn.use-var(radius, default) * 0.35);
23
+ height: fit-content;
24
+
25
+ color: fn.use-var(text-color, main);
26
+ background: fn.use-var(bg-color, top);
27
+
28
+ /*
29
+ * Adapted from Radix UI Themes Kbd.
30
+ * Multi-layer box-shadow simulates a 3D keycap appearance.
31
+ */
32
+ box-shadow:
33
+ inset 0 0.05em
34
+ color-mix(in srgb, #{fn.use-var(bg-color, top)} 75%, #{fn.use-var(bg-color, black)}),
35
+ 0 0 0 0.05em color-mix(in srgb, #{fn.use-var(border, color)} 50%, transparent),
36
+ 0 0.08em 0.17em color-mix(in srgb, #{fn.use-var(shadow, color)} 60%, transparent);
37
+ }
@@ -0,0 +1 @@
1
+ import './style.scss'