@veltra/desktop 1.7.4 → 1.7.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.
- package/dist/components/calendar/utils.js +5 -5
- package/dist/components/calendar/utils.js.map +1 -1
- package/dist/components/code-editor/code-editor.js +94 -15
- package/dist/components/code-editor/code-editor.js.map +1 -1
- package/dist/components/code-editor/style.js +1 -0
- package/dist/components/code-editor/style2.css +61 -13
- package/dist/components/date-picker/date-picker.js +27 -4
- package/dist/components/date-picker/date-picker.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/types/code-editor.d.ts +3 -1
- package/dist/types/date-panel.d.ts +1 -1
- package/dist/types/date-picker.d.ts +10 -2
- package/dist/types/date-range-picker.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/package.json +5 -5
- package/src/components/calendar/utils.ts +8 -8
- package/src/components/code-editor/code-editor.vue +109 -18
- package/src/components/code-editor/style.scss +71 -14
- package/src/components/code-editor/style.ts +1 -0
- package/src/components/date-picker/__test__/date-picker.test.ts +235 -0
- package/src/components/date-picker/date-picker.vue +33 -3
- package/src/types/code-editor.ts +3 -1
- package/src/types/date-panel.ts +1 -1
- package/src/types/date-picker.ts +10 -1
- package/src/types/date-range-picker.ts +1 -1
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { date } from '@cat-kit/core'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { createApp, h, nextTick, ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
import UDatePicker from '../date-picker.vue'
|
|
6
|
+
|
|
7
|
+
function mountDatePicker(
|
|
8
|
+
props: Record<string, unknown> = {},
|
|
9
|
+
emits: Record<string, Function> = {}
|
|
10
|
+
) {
|
|
11
|
+
const host = document.createElement('div')
|
|
12
|
+
document.body.appendChild(host)
|
|
13
|
+
|
|
14
|
+
const model = ref(props.modelValue)
|
|
15
|
+
const app = createApp({
|
|
16
|
+
render() {
|
|
17
|
+
return h(UDatePicker, {
|
|
18
|
+
...props,
|
|
19
|
+
modelValue: model.value,
|
|
20
|
+
'onUpdate:modelValue': (value: unknown) => {
|
|
21
|
+
model.value = value
|
|
22
|
+
emits['update:modelValue']?.(value)
|
|
23
|
+
},
|
|
24
|
+
onChange: (date?: Date) => {
|
|
25
|
+
emits.change?.(date)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
app.component('Transition', (_, { slots }) => slots.default?.())
|
|
32
|
+
|
|
33
|
+
app.mount(host)
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
host,
|
|
37
|
+
model,
|
|
38
|
+
app,
|
|
39
|
+
unmount() {
|
|
40
|
+
app.unmount()
|
|
41
|
+
host.remove()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe('UDatePicker', () => {
|
|
47
|
+
it('defaults dataType to string and emits change with native Date', async () => {
|
|
48
|
+
let changedDate: Date | undefined
|
|
49
|
+
let updatedVal: unknown
|
|
50
|
+
|
|
51
|
+
const { host, unmount } = mountDatePicker(
|
|
52
|
+
{},
|
|
53
|
+
{
|
|
54
|
+
'update:modelValue': (val: unknown) => {
|
|
55
|
+
updatedVal = val
|
|
56
|
+
},
|
|
57
|
+
change: (d?: Date) => {
|
|
58
|
+
changedDate = d
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const inputEl = host.querySelector('input')
|
|
65
|
+
expect(inputEl).not.toBeNull()
|
|
66
|
+
inputEl?.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
|
|
67
|
+
await nextTick()
|
|
68
|
+
await nextTick()
|
|
69
|
+
|
|
70
|
+
const cell = document.querySelector('.u-date-panel__cell.is-current') as HTMLElement
|
|
71
|
+
expect(cell).not.toBeNull()
|
|
72
|
+
cell.click()
|
|
73
|
+
await nextTick()
|
|
74
|
+
|
|
75
|
+
expect(typeof updatedVal).toBe('string')
|
|
76
|
+
expect(updatedVal).toMatch(/^\d{4}-\d{2}-\d{2}$/)
|
|
77
|
+
expect(changedDate instanceof Date).toBe(true)
|
|
78
|
+
} finally {
|
|
79
|
+
unmount()
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('supports dataType="date" and emits native Date for modelValue and change', async () => {
|
|
84
|
+
let changedDate: Date | undefined
|
|
85
|
+
let updatedVal: unknown
|
|
86
|
+
|
|
87
|
+
const { host, unmount } = mountDatePicker(
|
|
88
|
+
{ dataType: 'date', valueFormat: 'yyyyMMdd' },
|
|
89
|
+
{
|
|
90
|
+
'update:modelValue': (val: unknown) => {
|
|
91
|
+
updatedVal = val
|
|
92
|
+
},
|
|
93
|
+
change: (d?: Date) => {
|
|
94
|
+
changedDate = d
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const inputEl = host.querySelector('input')
|
|
101
|
+
inputEl?.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
|
|
102
|
+
await nextTick()
|
|
103
|
+
await nextTick()
|
|
104
|
+
|
|
105
|
+
const cell = document.querySelector('.u-date-panel__cell.is-current') as HTMLElement
|
|
106
|
+
cell.click()
|
|
107
|
+
await nextTick()
|
|
108
|
+
|
|
109
|
+
expect(updatedVal instanceof Date).toBe(true)
|
|
110
|
+
expect(changedDate instanceof Date).toBe(true)
|
|
111
|
+
} finally {
|
|
112
|
+
unmount()
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('supports dataType="timestamp" and emits timestamp for modelValue and native Date for change', async () => {
|
|
117
|
+
let changedDate: Date | undefined
|
|
118
|
+
let updatedVal: unknown
|
|
119
|
+
|
|
120
|
+
const { host, unmount } = mountDatePicker(
|
|
121
|
+
{ dataType: 'timestamp', valueFormat: 'yyyyMMdd' },
|
|
122
|
+
{
|
|
123
|
+
'update:modelValue': (val: unknown) => {
|
|
124
|
+
updatedVal = val
|
|
125
|
+
},
|
|
126
|
+
change: (d?: Date) => {
|
|
127
|
+
changedDate = d
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const inputEl = host.querySelector('input')
|
|
134
|
+
inputEl?.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
|
|
135
|
+
await nextTick()
|
|
136
|
+
await nextTick()
|
|
137
|
+
|
|
138
|
+
const cell = document.querySelector('.u-date-panel__cell.is-current') as HTMLElement
|
|
139
|
+
cell.click()
|
|
140
|
+
await nextTick()
|
|
141
|
+
|
|
142
|
+
expect(typeof updatedVal).toBe('number')
|
|
143
|
+
expect(updatedVal).toBeGreaterThan(0)
|
|
144
|
+
expect(changedDate instanceof Date).toBe(true)
|
|
145
|
+
} finally {
|
|
146
|
+
unmount()
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('applies valueFormat when dataType="string"', async () => {
|
|
151
|
+
let changedDate: Date | undefined
|
|
152
|
+
let updatedVal: unknown
|
|
153
|
+
|
|
154
|
+
const { host, unmount } = mountDatePicker(
|
|
155
|
+
{ dataType: 'string', valueFormat: 'yyyy/MM/dd' },
|
|
156
|
+
{
|
|
157
|
+
'update:modelValue': (val: unknown) => {
|
|
158
|
+
updatedVal = val
|
|
159
|
+
},
|
|
160
|
+
change: (d?: Date) => {
|
|
161
|
+
changedDate = d
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const inputEl = host.querySelector('input')
|
|
168
|
+
inputEl?.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
|
|
169
|
+
await nextTick()
|
|
170
|
+
await nextTick()
|
|
171
|
+
|
|
172
|
+
const cell = document.querySelector('.u-date-panel__cell.is-current') as HTMLElement
|
|
173
|
+
cell.click()
|
|
174
|
+
await nextTick()
|
|
175
|
+
|
|
176
|
+
expect(typeof updatedVal).toBe('string')
|
|
177
|
+
expect(updatedVal).toMatch(/^\d{4}\/\d{2}\/\d{2}$/)
|
|
178
|
+
expect(changedDate instanceof Date).toBe(true)
|
|
179
|
+
} finally {
|
|
180
|
+
unmount()
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('emits undefined for modelValue and change when cleared', async () => {
|
|
185
|
+
let changedDate: Date | undefined = new Date()
|
|
186
|
+
let updatedVal: unknown = '2024-05-01'
|
|
187
|
+
|
|
188
|
+
const { host, unmount } = mountDatePicker(
|
|
189
|
+
{ modelValue: '2024-05-01', clearable: true },
|
|
190
|
+
{
|
|
191
|
+
'update:modelValue': (val: unknown) => {
|
|
192
|
+
updatedVal = val
|
|
193
|
+
},
|
|
194
|
+
change: (d?: Date) => {
|
|
195
|
+
changedDate = d
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const inputWrapper = host.querySelector('.u-input') as HTMLElement
|
|
202
|
+
inputWrapper.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }))
|
|
203
|
+
await new Promise((r) => setTimeout(r, 50))
|
|
204
|
+
|
|
205
|
+
const clearBtn = host.querySelector('.u-input__clear') as HTMLElement
|
|
206
|
+
expect(clearBtn).not.toBeNull()
|
|
207
|
+
clearBtn.click()
|
|
208
|
+
await nextTick()
|
|
209
|
+
|
|
210
|
+
expect(updatedVal).toBeUndefined()
|
|
211
|
+
expect(changedDate).toBeUndefined()
|
|
212
|
+
} finally {
|
|
213
|
+
unmount()
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
it('correctly parses modelValue of Date, timestamp, and custom formatted string', async () => {
|
|
218
|
+
const dateObj = new Date(2024, 4, 15)
|
|
219
|
+
const d1 = mountDatePicker({ modelValue: dateObj })
|
|
220
|
+
await nextTick()
|
|
221
|
+
expect(d1.host.querySelector('input')?.value).toBe('2024-05-15')
|
|
222
|
+
d1.unmount()
|
|
223
|
+
|
|
224
|
+
const ts = date('2024-06-20').timestamp
|
|
225
|
+
const d2 = mountDatePicker({ modelValue: ts })
|
|
226
|
+
await nextTick()
|
|
227
|
+
expect(d2.host.querySelector('input')?.value).toBe('2024-06-20')
|
|
228
|
+
d2.unmount()
|
|
229
|
+
|
|
230
|
+
const d3 = mountDatePicker({ modelValue: '20240725', valueFormat: 'yyyyMMdd' })
|
|
231
|
+
await nextTick()
|
|
232
|
+
expect(d3.host.querySelector('input')?.value).toBe('2024-07-25')
|
|
233
|
+
d3.unmount()
|
|
234
|
+
})
|
|
235
|
+
})
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
</template>
|
|
37
37
|
|
|
38
38
|
<script lang="ts" setup>
|
|
39
|
-
import { date,
|
|
39
|
+
import { date, Dater } from '@cat-kit/core'
|
|
40
40
|
import { useFormFallbackProps, useUserAction } from '@veltra/compositions'
|
|
41
41
|
import { Calendar } from '@veltra/icons/normal'
|
|
42
42
|
import { bem, FORM_EMPTY_CONTENT } from '@veltra/utils'
|
|
@@ -55,6 +55,7 @@ defineOptions({ name: 'UDatePicker', inheritAttrs: false })
|
|
|
55
55
|
const props = withDefaults(defineProps<DatePickerProps>(), {
|
|
56
56
|
placeholder: '选择日期',
|
|
57
57
|
type: 'date',
|
|
58
|
+
dataType: 'string',
|
|
58
59
|
disabled: undefined,
|
|
59
60
|
readonly: undefined,
|
|
60
61
|
clearable: true
|
|
@@ -91,11 +92,28 @@ const currentDate = shallowRef<Dater>()
|
|
|
91
92
|
|
|
92
93
|
const { userAction, isUserActive } = useUserAction()
|
|
93
94
|
|
|
95
|
+
function parseModelValue(val?: string | number | Date): Dater | undefined {
|
|
96
|
+
if (val == null || val === '') return undefined
|
|
97
|
+
if (val instanceof Date || typeof val === 'number') {
|
|
98
|
+
const d = date(val)
|
|
99
|
+
return isNaN(d.timestamp) ? undefined : d
|
|
100
|
+
}
|
|
101
|
+
if (typeof val === 'string') {
|
|
102
|
+
if (props.dataType === 'string' && props.valueFormat) {
|
|
103
|
+
const parsed = Dater.parse(val, props.valueFormat)
|
|
104
|
+
if (!isNaN(parsed.timestamp)) return parsed
|
|
105
|
+
}
|
|
106
|
+
const fallback = date(val)
|
|
107
|
+
return isNaN(fallback.timestamp) ? undefined : fallback
|
|
108
|
+
}
|
|
109
|
+
return undefined
|
|
110
|
+
}
|
|
111
|
+
|
|
94
112
|
watch(
|
|
95
113
|
() => props.modelValue,
|
|
96
114
|
(modelValue) => {
|
|
97
115
|
if (isUserActive()) return
|
|
98
|
-
currentDate.value =
|
|
116
|
+
currentDate.value = parseModelValue(modelValue)
|
|
99
117
|
},
|
|
100
118
|
{ immediate: true }
|
|
101
119
|
)
|
|
@@ -104,9 +122,20 @@ const displayedValue = computed(() => {
|
|
|
104
122
|
return currentDate.value?.format(formatStr.value) ?? ''
|
|
105
123
|
})
|
|
106
124
|
|
|
125
|
+
function formatModelValue(d: Dater) {
|
|
126
|
+
if (props.dataType === 'date') {
|
|
127
|
+
return d.raw
|
|
128
|
+
}
|
|
129
|
+
if (props.dataType === 'timestamp') {
|
|
130
|
+
return d.timestamp
|
|
131
|
+
}
|
|
132
|
+
return d.format(props.valueFormat ?? formatStr.value)
|
|
133
|
+
}
|
|
134
|
+
|
|
107
135
|
const commitSelectedDate = userAction((date: Dater) => {
|
|
108
136
|
currentDate.value = date
|
|
109
|
-
emit('update:modelValue', date
|
|
137
|
+
emit('update:modelValue', formatModelValue(date))
|
|
138
|
+
emit('change', date.raw)
|
|
110
139
|
})
|
|
111
140
|
|
|
112
141
|
async function handleSelectDate(date: Dater) {
|
|
@@ -117,5 +146,6 @@ async function handleSelectDate(date: Dater) {
|
|
|
117
146
|
function handleClear() {
|
|
118
147
|
currentDate.value = undefined
|
|
119
148
|
emit('update:modelValue', undefined)
|
|
149
|
+
emit('change', undefined)
|
|
120
150
|
}
|
|
121
151
|
</script>
|
package/src/types/code-editor.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type CodeEditorLang =
|
|
|
14
14
|
export interface CodeEditorProps extends Omit<FormComponentProps, 'size'> {
|
|
15
15
|
modelValue?: string
|
|
16
16
|
/**
|
|
17
|
-
* 可选语言列表。长度大于 1
|
|
17
|
+
* 可选语言列表。长度大于 1 时在顶部工具栏显示内置语言选择器;
|
|
18
18
|
* 仅一种语言时显示语言名称标签
|
|
19
19
|
*/
|
|
20
20
|
langs?: CodeEditorLang[]
|
|
@@ -26,6 +26,8 @@ export interface CodeEditorProps extends Omit<FormComponentProps, 'size'> {
|
|
|
26
26
|
suffix?: string
|
|
27
27
|
/** 是否使用暗色主题 */
|
|
28
28
|
dark?: boolean
|
|
29
|
+
/** 是否显示放大按钮,默认 true;false 时不渲染放大按钮 */
|
|
30
|
+
zoomable?: boolean
|
|
29
31
|
/**
|
|
30
32
|
* 默认显示的行数,用于撑起编辑器最小高度,超出后滚动
|
|
31
33
|
* @default 8
|
package/src/types/date-panel.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface DatePanelProps {
|
|
|
7
7
|
date?: Dater
|
|
8
8
|
rangeDate?: [Dater, Dater]
|
|
9
9
|
range?: boolean
|
|
10
|
-
disabledDate?: (date: Dater) => boolean
|
|
10
|
+
disabledDate?: (date: Dater, raw: Date) => boolean
|
|
11
11
|
type?: 'date' | 'month' | 'year'
|
|
12
12
|
size?: FormComponentProps['size']
|
|
13
13
|
}
|
package/src/types/date-picker.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Dater } from '@cat-kit/core'
|
|
2
2
|
import type { DeconstructValue, FormComponentProps } from '@veltra/utils'
|
|
3
3
|
|
|
4
|
+
/** 绑定值类型 */
|
|
5
|
+
export type DatePickerDataType = 'date' | 'timestamp' | 'string'
|
|
6
|
+
|
|
4
7
|
/** date-picker组件属性 */
|
|
5
8
|
export interface DatePickerProps extends FormComponentProps {
|
|
6
9
|
modelValue?: string | number | Date
|
|
@@ -12,8 +15,13 @@ export interface DatePickerProps extends FormComponentProps {
|
|
|
12
15
|
format?: string
|
|
13
16
|
/** 日期值格式化, 当没有指定时默认使用format属性,仅当值和显示的内容不一致时才需要使用到该属性 */
|
|
14
17
|
valueFormat?: string
|
|
18
|
+
/**
|
|
19
|
+
* 绑定值的类型,默认为字符串。
|
|
20
|
+
* 当 dataType 没有指定为字符串时,valueFormat 属性不生效
|
|
21
|
+
*/
|
|
22
|
+
dataType?: DatePickerDataType
|
|
15
23
|
/** 最小可选日期 */
|
|
16
|
-
disabledDate?: (date: Dater) => boolean
|
|
24
|
+
disabledDate?: (date: Dater, raw: Date) => boolean
|
|
17
25
|
/** 是否显示清除按钮 */
|
|
18
26
|
clearable?: boolean
|
|
19
27
|
}
|
|
@@ -21,6 +29,7 @@ export interface DatePickerProps extends FormComponentProps {
|
|
|
21
29
|
/** date-picker组件定义的事件 */
|
|
22
30
|
export interface DatePickerEmits {
|
|
23
31
|
(e: 'update:modelValue', value?: string | number | Date): void
|
|
32
|
+
(e: 'change', date?: Date): void
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
/** date-picker组件暴露的属性和方法(组件内部使用) */
|
|
@@ -13,7 +13,7 @@ export interface DateRangePickerProps extends FormComponentProps {
|
|
|
13
13
|
/** 日期值格式化, 当没有指定时默认使用format属性,仅当值和显示的内容不一致时才需要使用到该属性 */
|
|
14
14
|
valueFormat?: string
|
|
15
15
|
/** 最小可选日期 */
|
|
16
|
-
disabledDate?: (date: Dater) => boolean
|
|
16
|
+
disabledDate?: (date: Dater, raw: Date) => boolean
|
|
17
17
|
/** 是否显示清除按钮 */
|
|
18
18
|
clearable?: boolean
|
|
19
19
|
}
|