@veltra/desktop 1.6.1 → 1.6.3

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 (42) hide show
  1. package/dist/components/collapse/collapse-item.js +3 -3
  2. package/dist/components/collapse/collapse-item.js.map +1 -1
  3. package/dist/components/collapse/collapse.js +7 -5
  4. package/dist/components/collapse/collapse.js.map +1 -1
  5. package/dist/components/date-panel/use-date-select.js +10 -9
  6. package/dist/components/date-panel/use-date-select.js.map +1 -1
  7. package/dist/components/multi-select/multi-select.js.map +1 -1
  8. package/dist/components/number-input/number-input.js +22 -8
  9. package/dist/components/number-input/number-input.js.map +1 -1
  10. package/dist/components/palette/color-transform.js +36 -15
  11. package/dist/components/palette/color-transform.js.map +1 -1
  12. package/dist/components/palette/palette.js +3 -1
  13. package/dist/components/palette/palette.js.map +1 -1
  14. package/dist/components/rich-text-editor/toolbar.js +8 -4
  15. package/dist/components/rich-text-editor/toolbar.js.map +1 -1
  16. package/dist/node_modules/.bun/@codemirror_autocomplete@6.20.3/node_modules/@codemirror/autocomplete/dist/index.js +20 -14
  17. package/dist/node_modules/.bun/@codemirror_autocomplete@6.20.3/node_modules/@codemirror/autocomplete/dist/index.js.map +1 -1
  18. package/dist/node_modules/.bun/@codemirror_lang-html@6.4.11/node_modules/@codemirror/lang-html/dist/index.js +4 -2
  19. package/dist/node_modules/.bun/@codemirror_lang-html@6.4.11/node_modules/@codemirror/lang-html/dist/index.js.map +1 -1
  20. package/dist/node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js +26 -21
  21. package/dist/node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js.map +1 -1
  22. package/dist/node_modules/.bun/@codemirror_search@6.7.1/node_modules/@codemirror/search/dist/index.js +22 -18
  23. package/dist/node_modules/.bun/@codemirror_search@6.7.1/node_modules/@codemirror/search/dist/index.js.map +1 -1
  24. package/dist/node_modules/.bun/@codemirror_state@6.7.1/node_modules/@codemirror/state/dist/index.js +7 -6
  25. package/dist/node_modules/.bun/@codemirror_state@6.7.1/node_modules/@codemirror/state/dist/index.js.map +1 -1
  26. package/dist/node_modules/.bun/@codemirror_view@6.43.8/node_modules/@codemirror/view/dist/index.js +142 -117
  27. package/dist/node_modules/.bun/@codemirror_view@6.43.8/node_modules/@codemirror/view/dist/index.js.map +1 -1
  28. package/dist/node_modules/.bun/@lezer_common@1.5.2/node_modules/@lezer/common/dist/index.js +16 -11
  29. package/dist/node_modules/.bun/@lezer_common@1.5.2/node_modules/@lezer/common/dist/index.js.map +1 -1
  30. package/dist/node_modules/.bun/@lezer_lr@1.4.10/node_modules/@lezer/lr/dist/index.js +25 -15
  31. package/dist/node_modules/.bun/@lezer_lr@1.4.10/node_modules/@lezer/lr/dist/index.js.map +1 -1
  32. package/dist/types/collapse.d.ts +1 -1
  33. package/package.json +15 -6
  34. package/src/components/collapse/__test__/collapse.test.ts +30 -0
  35. package/src/components/collapse/collapse-item.vue +7 -7
  36. package/src/components/number-input/__test__/number-input.test.ts +38 -0
  37. package/src/components/number-input/number-input.vue +28 -14
  38. package/src/components/palette/__test__/color-transform.test.ts +35 -0
  39. package/src/components/palette/__test__/palette.test.ts +102 -0
  40. package/src/components/palette/color-transform.ts +35 -11
  41. package/src/components/palette/palette.vue +5 -1
  42. package/src/types/collapse.ts +1 -1
@@ -43,7 +43,7 @@ interface CollapseItemProps {
43
43
  * @default false
44
44
  */
45
45
  modelValue?: boolean;
46
- /** 标题文本(也可通过 #header 插槽自定义整个头部) */
46
+ /** 标题文本(也可通过 #header 插槽自定义标题区;展开图标始终保留,可用 expandIcon 替换) */
47
47
  title?: string;
48
48
  /** 是否禁用 */
49
49
  disabled?: boolean;
package/package.json CHANGED
@@ -1,6 +1,15 @@
1
1
  {
2
2
  "name": "@veltra/desktop",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
4
+ "homepage": "https://github.com/cabinet-fe/ultra-ui#readme",
5
+ "bugs": {
6
+ "url": "https://github.com/cabinet-fe/ultra-ui/issues"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/cabinet-fe/ultra-ui",
11
+ "directory": "packages/desktop"
12
+ },
4
13
  "files": [
5
14
  "dist",
6
15
  "src"
@@ -71,12 +80,12 @@
71
80
  "peerDependencies": {
72
81
  "@cat-kit/core": ">=1.1.8",
73
82
  "@cat-kit/fe": ">=1.1.8",
74
- "@veltra/compositions": "^1.6.1",
75
- "@veltra/directives": "^1.6.1",
83
+ "@veltra/compositions": "^1.6.3",
84
+ "@veltra/directives": "^1.6.3",
76
85
  "@veltra/icons": "^1.5.0",
77
- "@veltra/sheet-core": "^2.3.0",
78
- "@veltra/styles": "^1.6.1",
79
- "@veltra/utils": "^1.6.1",
86
+ "@veltra/sheet-core": "^2.3.1",
87
+ "@veltra/styles": "^1.6.3",
88
+ "@veltra/utils": "^1.6.3",
80
89
  "vue": ">=3.5.41"
81
90
  },
82
91
  "peerDependenciesMeta": {
@@ -223,11 +223,41 @@ describe('CollapseItem standalone', () => {
223
223
  expect(host.querySelector('.u-collapse__item')).toBeTruthy()
224
224
  expect(host.querySelector('.u-collapse__header')).toBeTruthy()
225
225
  expect(host.querySelector('.u-collapse__title')?.textContent).toBe('Standalone')
226
+ expect(host.querySelector('.u-collapse__icon')).toBeTruthy()
226
227
  } finally {
227
228
  unmount()
228
229
  }
229
230
  })
230
231
 
232
+ it('keeps the expand icon when #header slot is provided', async () => {
233
+ const host = document.createElement('div')
234
+ document.body.appendChild(host)
235
+
236
+ const app = createApp({
237
+ render() {
238
+ return h(
239
+ UCollapseItem,
240
+ { title: 'Fallback' },
241
+ { header: () => 'Custom header', default: () => 'Body' }
242
+ )
243
+ }
244
+ })
245
+ app.mount(host)
246
+
247
+ try {
248
+ await nextTick()
249
+ expect(host.querySelector('.u-collapse__title')?.textContent).toBe('Custom header')
250
+ expect(host.querySelector('.u-collapse__icon')).toBeTruthy()
251
+
252
+ ;(host.querySelector('.u-collapse__header') as HTMLElement).click()
253
+ await nextTick()
254
+ expect(host.querySelector('.u-collapse__header.is-active')).toBeTruthy()
255
+ } finally {
256
+ app.unmount()
257
+ host.remove()
258
+ }
259
+ })
260
+
231
261
  it('defaults to collapsed when no v-model is provided', async () => {
232
262
  const { host, model, unmount } = mountStandaloneItem({ title: 'Standalone' })
233
263
 
@@ -10,13 +10,13 @@
10
10
  @keydown.enter.prevent="handleClick"
11
11
  @keydown.space.prevent="handleClick"
12
12
  >
13
- <slot name="header" :is-active="isActive">
14
- <span :class="cls.e('title')">{{ title }}</span>
13
+ <span :class="cls.e('title')">
14
+ <slot name="header" :is-active="isActive">{{ title }}</slot>
15
+ </span>
15
16
 
16
- <span :class="cls.e('icon')">
17
- <UIcon><component :is="iconComponent" /></UIcon>
18
- </span>
19
- </slot>
17
+ <span :class="cls.e('icon')">
18
+ <UIcon><component :is="iconComponent" /></UIcon>
19
+ </span>
20
20
  </div>
21
21
  <div ref="wrapperEl" :class="cls.e('content-wrapper')" :aria-hidden="!isActive">
22
22
  <div v-if="contentMounted" :class="cls.e('content')">
@@ -42,7 +42,7 @@ const props = withDefaults(defineProps<CollapseItemProps>(), { modelValue: false
42
42
  const emit = defineEmits<CollapseItemEmits>()
43
43
 
44
44
  defineSlots<{
45
- /** 自定义整个头部,作用域参数 `isActive` 指示展开状态;缺省渲染 title + 展开图标 */
45
+ /** 自定义标题区,作用域参数 `isActive` 指示展开状态;缺省渲染 title。展开图标始终由组件渲染,活动态旋转 180° */
46
46
  header?: (scope: { isActive: boolean }) => any
47
47
  /** 折叠面板内容 */
48
48
  default?: () => any
@@ -57,4 +57,42 @@ describe('NumberInput', () => {
57
57
  unmount()
58
58
  }
59
59
  })
60
+
61
+ it('updates the input value immediately without animation when step is 1', async () => {
62
+ const { host, unmount } = mountNumberInput({ modelValue: 1, step: 1 })
63
+
64
+ try {
65
+ const input = host.querySelector('input')!
66
+ const [upIcon, downIcon] = Array.from(
67
+ host.querySelectorAll('.u-number-input__step .u-icon')
68
+ ) as HTMLElement[]
69
+
70
+ upIcon!.dispatchEvent(new MouseEvent('click', { bubbles: true }))
71
+ await nextTick()
72
+ expect(input.value).toBe('2')
73
+
74
+ downIcon!.dispatchEvent(new MouseEvent('click', { bubbles: true }))
75
+ await nextTick()
76
+ expect(input.value).toBe('1')
77
+ } finally {
78
+ unmount()
79
+ }
80
+ })
81
+
82
+ it('keeps the rolling animation when step is greater than 1', () => {
83
+ const { host, unmount } = mountNumberInput({ modelValue: 0, step: 5 })
84
+
85
+ try {
86
+ const input = host.querySelector('input')!
87
+ const [upIcon] = Array.from(
88
+ host.querySelectorAll('.u-number-input__step .u-icon')
89
+ ) as HTMLElement[]
90
+
91
+ upIcon!.dispatchEvent(new MouseEvent('click', { bubbles: true }))
92
+ // 动画通过 requestAnimationFrame 逐帧更新, 同步阶段不应直接跳到目标值
93
+ expect(input.value).toBe('0')
94
+ } finally {
95
+ unmount()
96
+ }
97
+ })
60
98
  })
@@ -301,6 +301,28 @@ const tween = new Tween(
301
301
  }
302
302
  )
303
303
 
304
+ /**
305
+ * 步进后更新输入框的展示值
306
+ * 步长为 1 时直接展示目标值, 不播放数字滚动动画
307
+ * @param rawFrom 步进前的原始值(存在倍数时为除以倍数后的值)
308
+ * @param rawTarget 步进后的原始值(存在倍数时为除以倍数后的值)
309
+ */
310
+ function updateStepDisplay(rawFrom: number, rawTarget: number): void {
311
+ tween.state.n = rawFrom
312
+
313
+ if (stepVal.value === 1) {
314
+ tween.state.n = rawTarget
315
+ const _rawInput = inputDom.value
316
+ if (!_rawInput) return
317
+ const { multiple } = props
318
+ _rawInput.value = getDisplayed(multiple ? $n.mul(rawTarget, multiple) : rawTarget)
319
+ return
320
+ }
321
+
322
+ // tween 动画在原始值上进行
323
+ tween.to({ n: rawTarget })
324
+ }
325
+
304
326
  /** 增 */
305
327
  function increase(): void {
306
328
  if (disabled.value) return
@@ -313,17 +335,13 @@ function increase(): void {
313
335
  // 在原始值基础上加步长
314
336
  const newRawVal = $n.plus(rawVal, stepVal.value)
315
337
  // 乘以倍数得到新的实际值
316
- const newVal = $n.mul(newRawVal, multiple)
317
- const target = getValidValue(newVal)
338
+ const target = getValidValue($n.mul(newRawVal, multiple))
318
339
  model.value = target
319
- // tween 动画在原始值上进行
320
- tween.state.n = $n.div(target, multiple)
321
- tween.to({ n: $n.div(target, multiple) })
340
+ updateStepDisplay($n.div(val, multiple), $n.div(target, multiple))
322
341
  } else {
323
- tween.state.n = val
324
342
  const target = getValidValue($n.plus(val, stepVal.value))
325
343
  model.value = target
326
- tween.to({ n: target })
344
+ updateStepDisplay(val, target)
327
345
  }
328
346
  }
329
347
 
@@ -339,17 +357,13 @@ function decrease(): void {
339
357
  // 在原始值基础上减步长
340
358
  const newRawVal = $n.minus(rawVal, stepVal.value)
341
359
  // 乘以倍数得到新的实际值
342
- const newVal = $n.mul(newRawVal, multiple)
343
- const target = getValidValue(newVal)
360
+ const target = getValidValue($n.mul(newRawVal, multiple))
344
361
  model.value = target
345
- // tween 动画在原始值上进行
346
- tween.state.n = $n.div(target, multiple)
347
- tween.to({ n: $n.div(target, multiple) })
362
+ updateStepDisplay($n.div(val, multiple), $n.div(target, multiple))
348
363
  } else {
349
- tween.state.n = val
350
364
  const target = getValidValue($n.minus(val, stepVal.value))
351
365
  model.value = target
352
- tween.to({ n: target })
366
+ updateStepDisplay(val, target)
353
367
  }
354
368
  }
355
369
 
@@ -0,0 +1,35 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { HEX2RGBA } from '../color-transform'
4
+
5
+ describe('HEX2RGBA', () => {
6
+ it('解析 6 位 HEX(# 可省略、大小写不敏感)', () => {
7
+ expect(HEX2RGBA('#FF0000')).toEqual({ RGB: { r: 255, g: 0, b: 0 }, alpha: 1 })
8
+ expect(HEX2RGBA('ff0000')).toEqual({ RGB: { r: 255, g: 0, b: 0 }, alpha: 1 })
9
+ expect(HEX2RGBA(' #00ff00 ')).toEqual({ RGB: { r: 0, g: 255, b: 0 }, alpha: 1 })
10
+ })
11
+
12
+ it('解析 3/4 位缩写 HEX', () => {
13
+ expect(HEX2RGBA('#f00')).toEqual({ RGB: { r: 255, g: 0, b: 0 }, alpha: 1 })
14
+ expect(HEX2RGBA('#f008')).toEqual({ RGB: { r: 255, g: 0, b: 0 }, alpha: 0x88 / 255 })
15
+ })
16
+
17
+ it('解析 8 位 HEX(RRGGBBAA)', () => {
18
+ expect(HEX2RGBA('#FF000080')).toEqual({ RGB: { r: 255, g: 0, b: 0 }, alpha: 0x80 / 255 })
19
+ })
20
+
21
+ it('解析 rgb() / rgba() 函数表示', () => {
22
+ expect(HEX2RGBA('rgb(255, 0, 0)')).toEqual({ RGB: { r: 255, g: 0, b: 0 }, alpha: 1 })
23
+ expect(HEX2RGBA('rgba(255,0,0,0.5)')).toEqual({ RGB: { r: 255, g: 0, b: 0 }, alpha: 0.5 })
24
+ expect(HEX2RGBA('rgba(255, 0, 0, 50%)')).toEqual({ RGB: { r: 255, g: 0, b: 0 }, alpha: 0.5 })
25
+ })
26
+
27
+ it('非法输入返回 null', () => {
28
+ expect(HEX2RGBA('')).toBeNull()
29
+ expect(HEX2RGBA('#FF')).toBeNull()
30
+ expect(HEX2RGBA('#GGGGGG')).toBeNull()
31
+ expect(HEX2RGBA('red')).toBeNull()
32
+ expect(HEX2RGBA('rgb(256, 0, 0)')).toBeNull()
33
+ expect(HEX2RGBA('rgba(0, 0, 0, 2)')).toBeNull()
34
+ })
35
+ })
@@ -0,0 +1,102 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { createApp, h, nextTick, ref } from 'vue'
3
+
4
+ import UPalette from '../palette.vue'
5
+
6
+ function mountPalette(initialColor?: string) {
7
+ const host = document.createElement('div')
8
+ document.body.appendChild(host)
9
+
10
+ const model = ref(initialColor)
11
+ const app = createApp({
12
+ render() {
13
+ return h(UPalette, {
14
+ modelValue: model.value,
15
+ 'onUpdate:modelValue': (value: string | undefined) => {
16
+ model.value = value
17
+ }
18
+ })
19
+ }
20
+ })
21
+
22
+ app.mount(host)
23
+
24
+ return {
25
+ host,
26
+ model,
27
+ setModel(value: string) {
28
+ model.value = value
29
+ },
30
+ async unmount() {
31
+ // 等待 usePop 打开面板时的异步定位完成,避免卸载后回调访问已销毁元素
32
+ await new Promise((resolve) => setTimeout(resolve, 0))
33
+ app.unmount()
34
+ await nextTick()
35
+ host.remove()
36
+ }
37
+ }
38
+ }
39
+
40
+ async function openPanel(host: HTMLElement) {
41
+ host.querySelector('.u-palette')?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
42
+ await nextTick()
43
+ await nextTick()
44
+ }
45
+
46
+ describe('UPalette', () => {
47
+ beforeEach(() => {
48
+ // happy-dom 中 offsetWidth/offsetHeight 恒为 0,这里模拟 200x200 的画布
49
+ vi.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockReturnValue(200)
50
+ vi.spyOn(HTMLElement.prototype, 'offsetHeight', 'get').mockReturnValue(200)
51
+
52
+ // usePop 的浮层容器是模块级单例,上一个用例清空 body 后需补回 teleport 目标
53
+ if (!document.getElementById('pop-container')) {
54
+ const popContainer = document.createElement('div')
55
+ popContainer.id = 'pop-container'
56
+ document.body.appendChild(popContainer)
57
+ }
58
+ })
59
+
60
+ afterEach(() => {
61
+ vi.restoreAllMocks()
62
+ document.body.innerHTML = ''
63
+ })
64
+
65
+ it('SV 面板 thumb 跟随初始绑定的颜色', async () => {
66
+ const { host, unmount } = mountPalette('#FF0000')
67
+
68
+ try {
69
+ await openPanel(host)
70
+
71
+ const thumb = document.body.querySelector<HTMLElement>('.u-palette__sv-thumb')
72
+ expect(thumb).not.toBeNull()
73
+ // #FF0000 → hsv(0, 1, 1),thumb 应在右上角: translate(200px, 0px)
74
+ expect(thumb!.style.transform).toBe('translate(200px, 0px)')
75
+
76
+ const hueThumb = document.body.querySelector<HTMLElement>('.u-palette__hue-thumb')
77
+ expect(hueThumb!.style.transform).toBe('translateX(0px)')
78
+ } finally {
79
+ unmount()
80
+ }
81
+ })
82
+
83
+ it('面板关闭时外部 modelValue 变化,打开后 thumb 跟随', async () => {
84
+ const { host, setModel, unmount } = mountPalette('')
85
+
86
+ try {
87
+ setModel('#0000FF')
88
+ await nextTick()
89
+
90
+ await openPanel(host)
91
+
92
+ const thumb = document.body.querySelector<HTMLElement>('.u-palette__sv-thumb')
93
+ // #0000FF → hsv(240, 1, 1),thumb 应在右上角: translate(200px, 0px)
94
+ expect(thumb!.style.transform).toBe('translate(200px, 0px)')
95
+
96
+ const hueThumb = document.body.querySelector<HTMLElement>('.u-palette__hue-thumb')
97
+ expect(hueThumb!.style.transform).toBe(`translateX(${(200 * 240) / 360}px)`)
98
+ } finally {
99
+ unmount()
100
+ }
101
+ })
102
+ })
@@ -103,18 +103,35 @@ export function RGB2HSV(RGB: PaletteRGB): PaletteHSV {
103
103
  }
104
104
 
105
105
  /**
106
- * 将 HEX 颜色转换为 RGB 颜色
107
- * @param hex HEX 颜色
108
- * @returns RGB 颜色
106
+ * 将颜色字符串转换为 RGBA 颜色
107
+ * @description
108
+ * 支持 `#rgb`、`#rgba`、`#rrggbb`、`#rrggbbaa`(`#` 可省略)以及 `rgb()` / `rgba()`
109
+ * 函数表示;无法解析时返回 `null`,调用方应视为未绑定颜色而不是沿用错误的解析结果。
110
+ * @param color 颜色字符串
111
+ * @returns RGBA 颜色,解析失败返回 null
109
112
  */
110
- export function HEX2RGBA(hex: string): { RGB: PaletteRGB; alpha: number } {
111
- const rgb = { r: 0, g: 0, b: 0 }
112
- let alpha = 1
113
+ export function HEX2RGBA(color: string): { RGB: PaletteRGB; alpha: number } | null {
114
+ const input = color.trim()
115
+
116
+ // rgb() / rgba() 函数表示
117
+ const fnMatch = input.match(
118
+ /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*(\d+(?:\.\d+)?%?)\s*)?\)$/i
119
+ )
120
+ if (fnMatch) {
121
+ const [, r, g, b, a] = fnMatch
122
+ const rgb = { r: +r!, g: +g!, b: +b! }
123
+ if (rgb.r > 255 || rgb.g > 255 || rgb.b > 255) return null
113
124
 
114
- if (hex.startsWith('#')) {
115
- hex = hex.slice(1)
125
+ let alpha = 1
126
+ if (a !== undefined) {
127
+ alpha = a.endsWith('%') ? parseFloat(a) / 100 : parseFloat(a)
128
+ if (!Number.isFinite(alpha) || alpha < 0 || alpha > 1) return null
129
+ }
130
+ return { RGB: rgb, alpha }
116
131
  }
117
132
 
133
+ let hex = input.startsWith('#') ? input.slice(1) : input
134
+
118
135
  if (hex.length === 3 || hex.length === 4) {
119
136
  hex = hex
120
137
  .split('')
@@ -122,13 +139,20 @@ export function HEX2RGBA(hex: string): { RGB: PaletteRGB; alpha: number } {
122
139
  .join('')
123
140
  }
124
141
 
142
+ if (hex.length !== 6 && hex.length !== 8) return null
143
+ if (!/^[0-9a-fA-F]+$/.test(hex)) return null
144
+
145
+ let alpha = 1
125
146
  const alphaStr = hex.slice(6)
126
147
  if (alphaStr) {
127
148
  alpha = parseInt(alphaStr, 16) / 255
128
149
  }
129
- rgb.r = parseInt(hex.slice(0, 2), 16)
130
- rgb.g = parseInt(hex.slice(2, 4), 16)
131
- rgb.b = parseInt(hex.slice(4, 6), 16)
150
+
151
+ const rgb = {
152
+ r: parseInt(hex.slice(0, 2), 16),
153
+ g: parseInt(hex.slice(2, 4), 16),
154
+ b: parseInt(hex.slice(4, 6), 16)
155
+ }
132
156
 
133
157
  return { RGB: rgb, alpha }
134
158
  }
@@ -94,7 +94,11 @@ watch(
94
94
  if (!color) return
95
95
  if (isUserActive()) return
96
96
 
97
- const { RGB, alpha } = HEX2RGBA(color)
97
+ const parsed = HEX2RGBA(color)
98
+ // 无法解析的颜色(如非法字符)视为未绑定,跳过同步,避免把 NaN 写回 modelValue
99
+ if (!parsed) return
100
+
101
+ const { RGB, alpha } = parsed
98
102
  const hsv = RGB2HSV(RGB)
99
103
  rest.updateHue(hsv.h)
100
104
  rest.updateSV({ s: hsv.s, v: hsv.v })
@@ -52,7 +52,7 @@ export interface CollapseItemProps {
52
52
  */
53
53
  modelValue?: boolean
54
54
 
55
- /** 标题文本(也可通过 #header 插槽自定义整个头部) */
55
+ /** 标题文本(也可通过 #header 插槽自定义标题区;展开图标始终保留,可用 expandIcon 替换) */
56
56
  title?: string
57
57
 
58
58
  /** 是否禁用 */