@zfqh/uview-x 0.0.1 → 0.0.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.0.3](http://10.10.10.16/caoben/front-end-tools/compare/@zfqh/uview-x@0.0.2...@zfqh/uview-x@0.0.3) (2026-11-10)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * 文本框输入过快光标乱跳 ([fdf27f9](http://10.10.10.16/caoben/front-end-tools/commits/fdf27f9c37cde6941af3e8dd2db0c708816d3a2f))
12
+
13
+
14
+
15
+
16
+
17
+ ## [0.0.2](http://10.10.10.16/caoben/front-end-tools/compare/@zfqh/uview-x@0.0.1...@zfqh/uview-x@0.0.2) (2026-11-09)
18
+
19
+ **Note:** Version bump only for package @zfqh/uview-x
20
+
21
+
22
+
23
+
24
+
6
25
  ## 0.0.1 (2026-11-09)
7
26
 
8
27
  **Note:** Version bump only for package @zfqh/uview-x
@@ -118,6 +118,11 @@ const iconStyle = computed(() => {
118
118
  };
119
119
  if (p.color && !colorType.includes(p.color)) {
120
120
  style.color = p.color;
121
+ }
122
+ if (p.customPrefix !== 'uvicon') {
123
+ style.fontFamily = p.customPrefix;
124
+ } else {
125
+ style.fontFamily = 'uvicon-iconfont';
121
126
  }
122
127
  return style;
123
128
  });
@@ -168,7 +173,6 @@ const clickHandler = (e: any) => {
168
173
  justify-content: center;
169
174
  }
170
175
  .uv-icon__icon {
171
- font-family: uvicon-iconfont;
172
176
  position: relative;
173
177
  /* #ifndef APP-NVUE */
174
178
  display: flex;
@@ -139,9 +139,16 @@
139
139
  'keyboardheightchange'
140
140
  ])
141
141
 
142
- // 输入框的值
143
- const innerValue = ref<any>(p.modelValue !== '' ? p.modelValue : p.value)
142
+ const toValueString = (value: any): string => (value === null || value === undefined ? '' : String(value))
143
+
144
+ const initialValue = toValueString(p.modelValue !== '' ? p.modelValue : p.value)
145
+ // 绑定到 input 的 value:只表示初始值/外部赋值/formatter 结果,输入过程中不回写,避免光标跳动
146
+ const innerValue = ref<string>(initialValue)
144
147
  const inputRef = ref<any>(null)
148
+ // 输入框当前显示值:非响应式,仅用于回写判断与事件 payload,避免每次输入触发组件渲染
149
+ let currentValue = initialValue
150
+ // 是否有内容:仅用于清除控件显隐,空/非空切换时才触发渲染
151
+ const hasValue = ref<boolean>(initialValue !== '')
145
152
  // 是否处于获得焦点状态
146
153
  const focused = ref(false)
147
154
  // 过滤处理方法
@@ -150,19 +157,50 @@
150
157
  // 表单域上下文:用于触发表单校验(provide/inject 替代 $parent 链)
151
158
  const formItemContext: any = inject('uv-form-item-context', null)
152
159
 
153
- watch(
154
- () => p.modelValue,
155
- (newVal: any) => {
156
- innerValue.value = newVal
157
- }
158
- )
160
+ const updateHasValue = (value: string) => {
161
+ const next = value !== ''
162
+ if (hasValue.value !== next) hasValue.value = next
163
+ }
159
164
 
160
- watch(
161
- () => p.value,
162
- (newVal: any) => {
163
- innerValue.value = newVal
165
+ // 让 input 的 value 属性真正变化:绑定值已是目标值时,用当前显示值制造一次变化
166
+ const setBoundValue = (value: string) => {
167
+ if (innerValue.value !== value) {
168
+ innerValue.value = value
169
+ return
170
+ }
171
+ if (currentValue !== value) {
172
+ const display = currentValue
173
+ innerValue.value = display
174
+ nextTick(() => {
175
+ if (innerValue.value !== value) innerValue.value = value
176
+ })
164
177
  }
165
- )
178
+ }
179
+
180
+ // 写入输入框并同步内部当前值(外部赋值、formatter 生效、清空时使用)
181
+ const applyValue = (value: string) => {
182
+ setBoundValue(value)
183
+ currentValue = value
184
+ updateHasValue(value)
185
+ }
186
+
187
+ // 在下一 tick 统一向上抛出事件,保持 uv-ui 事件顺序
188
+ const emitValue = (value: string) => {
189
+ nextTick(() => {
190
+ emit('input', value)
191
+ emit('update:modelValue', value)
192
+ emit('change', value)
193
+ // 尝试调用uv-form的验证方法
194
+ if (formItemContext) formItemContext.validate('change')
195
+ })
196
+ }
197
+
198
+ watch([() => p.modelValue, () => p.value], () => {
199
+ const next = toValueString(p.modelValue !== '' ? p.modelValue : p.value)
200
+ // 忽略 v-model 回传的当前输入值,避免受控回写覆盖原生输入
201
+ if (next === currentValue) return
202
+ applyValue(next)
203
+ })
166
204
 
167
205
  // x 的 input type 无 password 枚举值,密码由 password 属性控制
168
206
  const inputType = computed(() => (p.type === 'password' ? 'text' : p.type))
@@ -202,7 +240,7 @@
202
240
 
203
241
  // 是否显示清除控件
204
242
  const isShowClear = computed(() => {
205
- return !!p.clearable && !p.readonly && focused.value && innerValue.value !== ''
243
+ return !!p.clearable && !p.readonly && focused.value && hasValue.value
206
244
  })
207
245
 
208
246
  // 组件的类名
@@ -261,22 +299,27 @@
261
299
 
262
300
  // 当键盘输入时,触发input事件
263
301
  const onInput = (e: any) => {
264
- const value = (e && e.detail ? e.detail.value : '') || ''
302
+ const rawValue = toValueString(e && e.detail ? e.detail.value : '')
265
303
  // 格式化过滤方法
266
304
  const formatter = p.formatter || innerFormatter.value
267
- const formatValue = typeof formatter === 'function' ? formatter(value) : value
268
- // 为了避免props的单向数据流特性,需要先将innerValue值设置为当前值,再在nextTick中重新赋予设置后的值才有效
269
- innerValue.value = value
270
- nextTick(() => {
271
- innerValue.value = formatValue
272
- valueChange()
273
- })
305
+ const formatValue = typeof formatter === 'function' ? toValueString(formatter(rawValue)) : rawValue
306
+ // 仅当 formatter 真正改变内容时才回写 value,恒等时保持原生输入不受打断
307
+ if (formatValue !== rawValue) {
308
+ currentValue = rawValue
309
+ applyValue(formatValue)
310
+ } else {
311
+ currentValue = formatValue
312
+ updateHasValue(formatValue)
313
+ }
314
+ emitValue(formatValue)
274
315
  }
275
316
 
276
317
  // 输入框失去焦点时触发
277
318
  const onBlur = (event: any) => {
278
- const value = event && event.detail ? event.detail.value : ''
279
- emit('blur', value)
319
+ const detailValue = event && event.detail && event.detail.value !== undefined ? event.detail.value : currentValue
320
+ currentValue = toValueString(detailValue)
321
+ updateHasValue(currentValue)
322
+ emit('blur', currentValue)
280
323
  // H5端的blur会先于点击清除控件的点击click事件触发,导致focused
281
324
  // 瞬间为false,从而隐藏了清除控件而无法被点击到
282
325
  $uv.sleep(100).then(() => {
@@ -287,14 +330,20 @@
287
330
  }
288
331
 
289
332
  // 输入框聚焦时触发
290
- const onFocus = () => {
333
+ const onFocus = (event: any) => {
291
334
  focused.value = true
335
+ const detailValue = event && event.detail && event.detail.value !== undefined ? event.detail.value : currentValue
336
+ currentValue = toValueString(detailValue)
337
+ updateHasValue(currentValue)
292
338
  emit('focus')
293
339
  }
294
340
 
295
341
  // 点击完成按钮时触发
296
- const onConfirm = () => {
297
- emit('confirm', innerValue.value)
342
+ const onConfirm = (event: any) => {
343
+ const detailValue = event && event.detail && event.detail.value !== undefined ? event.detail.value : currentValue
344
+ currentValue = toValueString(detailValue)
345
+ updateHasValue(currentValue)
346
+ emit('confirm', currentValue)
298
347
  }
299
348
 
300
349
  // 键盘高度发生变化的时候触发此事件
@@ -302,22 +351,10 @@
302
351
  emit('keyboardheightchange', e)
303
352
  }
304
353
 
305
- // 内容发生变化,进行处理
306
- const valueChange = () => {
307
- const value = innerValue.value
308
- nextTick(() => {
309
- emit('input', value)
310
- emit('update:modelValue', value)
311
- emit('change', value)
312
- // 尝试调用uv-form的验证方法
313
- if (formItemContext) formItemContext.validate('change')
314
- })
315
- }
316
-
317
354
  // 点击清除控件
318
355
  const onClear = () => {
319
- innerValue.value = ''
320
- valueChange()
356
+ applyValue('')
357
+ emitValue('')
321
358
  nextTick(() => {
322
359
  emit('clear')
323
360
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zfqh/uview-x",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "uview-x:面向 uni-app x 蒸汽模式(Vapor)、经典 uni-app Vue3 与 nvue 的 UV UI 组件库,组件保持 uv-ui API。",
5
5
  "main": "index.ts",
6
6
  "types": "types/index.d.ts",
@@ -40,5 +40,5 @@
40
40
  "README.md",
41
41
  "CHANGELOG.md"
42
42
  ],
43
- "gitHead": "6ffb8b8188f29e8d4af167895737c9cb7bc0997f"
43
+ "gitHead": "dd6ce3c2c137ce266d088dd66f539e95bd7379ae"
44
44
  }