@zfqh/uview-x 0.0.2 → 0.0.4

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.4](http://10.10.10.16/caoben/front-end-tools/compare/@zfqh/uview-x@0.0.3...@zfqh/uview-x@0.0.4) (2026-11-10)
7
+
8
+ **Note:** Version bump only for package @zfqh/uview-x
9
+
10
+
11
+
12
+
13
+
14
+ ## [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)
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * 文本框输入过快光标乱跳 ([fdf27f9](http://10.10.10.16/caoben/front-end-tools/commits/fdf27f9c37cde6941af3e8dd2db0c708816d3a2f))
20
+
21
+
22
+
23
+
24
+
6
25
  ## [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)
7
26
 
8
27
  **Note:** Version bump only for package @zfqh/uview-x
package/README.md CHANGED
@@ -37,6 +37,28 @@ export function createApp() {
37
37
  }
38
38
  ```
39
39
 
40
+ 以上 easycom 配置适用于 dev 阶段(H5 / 小程序 / 经典 uni-app);uni-app x Vapor 的 build 阶段不支持 easycom,见下方显式 import。
41
+
42
+ ### 显式 import(推荐;Vapor build 必需)
43
+
44
+ uni-app x Vapor(蒸汽模式)下,easycom 仅在 dev 阶段可用,build 阶段不支持;用户也可能主动关闭 easycom。为此 `components/uv-*` 内部已经全部改为显式 import,页面只需 import 自己用到的组件:
45
+
46
+ ```vue
47
+ <script setup lang="ts">
48
+ import UvButton from '@zfqh/uview-x/components/uv-button/uv-button.vue';
49
+ import UvIcon from '@zfqh/uview-x/components/uv-icon/uv-icon.vue';
50
+ </script>
51
+
52
+ <template>
53
+ <uv-button type="primary" text="提交" @click="onSubmit" />
54
+ <uv-icon name="search" :size="24" />
55
+ </template>
56
+ ```
57
+
58
+ - 组件入口为 `@zfqh/uview-x/components/uv-<name>/uv-<name>.vue`,与 easycom 路径一致。
59
+ - `examples/uview-vapor`、`examples/uniapp-vapor` 已关闭 easycom(`"easycom": { "autoscan": false }`),用于验证显式 import 在 dev / build 下都可用。
60
+ - `app.use(uviewX)` 只安装 `$uv` 插件能力,不注册组件。
61
+
40
62
  `uni.scss` 引入主题变量,`App.uvue` 引入全局工具样式:
41
63
 
42
64
  ```scss
@@ -114,6 +114,8 @@
114
114
  import { computed } from 'vue'
115
115
  import { $uv } from '../../utils'
116
116
  import props from './props'
117
+ import UvIcon from '../uv-icon/uv-icon.vue'
118
+ import UvLoadingIcon from '../uv-loading-icon/uv-loading-icon.vue'
117
119
  import { useThrottle } from '../../hooks'
118
120
  import { createBem } from '../../utils/compat'
119
121
 
@@ -42,6 +42,7 @@
42
42
  import { computed, inject, nextTick, onUnmounted, ref, watch } from 'vue'
43
43
  import { $uv } from '../../utils'
44
44
  import props from './props'
45
+ import UvIcon from '../uv-icon/uv-icon.vue'
45
46
  import { preventEvent } from '../../utils/compat'
46
47
 
47
48
 
@@ -96,6 +96,8 @@
96
96
  import { computed, nextTick, onMounted, ref, watch } from 'vue'
97
97
  import { $uv } from '../../utils'
98
98
  import props from './props'
99
+ import UvPopup from '../uv-popup/uv-popup.vue'
100
+ import UvButton from '../uv-button/uv-button.vue'
99
101
  import { useI18n } from '../../hooks'
100
102
 
101
103
  defineOptions({
@@ -71,6 +71,9 @@
71
71
  import { computed, inject, onUnmounted, provide, reactive, ref, watch } from 'vue'
72
72
  import { $uv } from '../../utils'
73
73
  import props from './props'
74
+ import UvIcon from '../uv-icon/uv-icon.vue'
75
+ import UvTransition from '../uv-transition/uv-transition.vue'
76
+ import UvLine from '../uv-line/uv-line.vue'
74
77
 
75
78
 
76
79
  defineOptions({
@@ -70,6 +70,7 @@
70
70
  import { computed, inject, nextTick, ref, watch } from 'vue'
71
71
  import { $uv } from '../../utils'
72
72
  import props from './props'
73
+ import UvIcon from '../uv-icon/uv-icon.vue'
73
74
 
74
75
 
75
76
  defineOptions({
@@ -139,9 +140,16 @@
139
140
  'keyboardheightchange'
140
141
  ])
141
142
 
142
- // 输入框的值
143
- const innerValue = ref<any>(p.modelValue !== '' ? p.modelValue : p.value)
143
+ const toValueString = (value: any): string => (value === null || value === undefined ? '' : String(value))
144
+
145
+ const initialValue = toValueString(p.modelValue !== '' ? p.modelValue : p.value)
146
+ // 绑定到 input 的 value:只表示初始值/外部赋值/formatter 结果,输入过程中不回写,避免光标跳动
147
+ const innerValue = ref<string>(initialValue)
144
148
  const inputRef = ref<any>(null)
149
+ // 输入框当前显示值:非响应式,仅用于回写判断与事件 payload,避免每次输入触发组件渲染
150
+ let currentValue = initialValue
151
+ // 是否有内容:仅用于清除控件显隐,空/非空切换时才触发渲染
152
+ const hasValue = ref<boolean>(initialValue !== '')
145
153
  // 是否处于获得焦点状态
146
154
  const focused = ref(false)
147
155
  // 过滤处理方法
@@ -150,19 +158,50 @@
150
158
  // 表单域上下文:用于触发表单校验(provide/inject 替代 $parent 链)
151
159
  const formItemContext: any = inject('uv-form-item-context', null)
152
160
 
153
- watch(
154
- () => p.modelValue,
155
- (newVal: any) => {
156
- innerValue.value = newVal
157
- }
158
- )
161
+ const updateHasValue = (value: string) => {
162
+ const next = value !== ''
163
+ if (hasValue.value !== next) hasValue.value = next
164
+ }
159
165
 
160
- watch(
161
- () => p.value,
162
- (newVal: any) => {
163
- innerValue.value = newVal
166
+ // 让 input 的 value 属性真正变化:绑定值已是目标值时,用当前显示值制造一次变化
167
+ const setBoundValue = (value: string) => {
168
+ if (innerValue.value !== value) {
169
+ innerValue.value = value
170
+ return
171
+ }
172
+ if (currentValue !== value) {
173
+ const display = currentValue
174
+ innerValue.value = display
175
+ nextTick(() => {
176
+ if (innerValue.value !== value) innerValue.value = value
177
+ })
164
178
  }
165
- )
179
+ }
180
+
181
+ // 写入输入框并同步内部当前值(外部赋值、formatter 生效、清空时使用)
182
+ const applyValue = (value: string) => {
183
+ setBoundValue(value)
184
+ currentValue = value
185
+ updateHasValue(value)
186
+ }
187
+
188
+ // 在下一 tick 统一向上抛出事件,保持 uv-ui 事件顺序
189
+ const emitValue = (value: string) => {
190
+ nextTick(() => {
191
+ emit('input', value)
192
+ emit('update:modelValue', value)
193
+ emit('change', value)
194
+ // 尝试调用uv-form的验证方法
195
+ if (formItemContext) formItemContext.validate('change')
196
+ })
197
+ }
198
+
199
+ watch([() => p.modelValue, () => p.value], () => {
200
+ const next = toValueString(p.modelValue !== '' ? p.modelValue : p.value)
201
+ // 忽略 v-model 回传的当前输入值,避免受控回写覆盖原生输入
202
+ if (next === currentValue) return
203
+ applyValue(next)
204
+ })
166
205
 
167
206
  // x 的 input type 无 password 枚举值,密码由 password 属性控制
168
207
  const inputType = computed(() => (p.type === 'password' ? 'text' : p.type))
@@ -202,7 +241,7 @@
202
241
 
203
242
  // 是否显示清除控件
204
243
  const isShowClear = computed(() => {
205
- return !!p.clearable && !p.readonly && focused.value && innerValue.value !== ''
244
+ return !!p.clearable && !p.readonly && focused.value && hasValue.value
206
245
  })
207
246
 
208
247
  // 组件的类名
@@ -261,22 +300,27 @@
261
300
 
262
301
  // 当键盘输入时,触发input事件
263
302
  const onInput = (e: any) => {
264
- const value = (e && e.detail ? e.detail.value : '') || ''
303
+ const rawValue = toValueString(e && e.detail ? e.detail.value : '')
265
304
  // 格式化过滤方法
266
305
  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
- })
306
+ const formatValue = typeof formatter === 'function' ? toValueString(formatter(rawValue)) : rawValue
307
+ // 仅当 formatter 真正改变内容时才回写 value,恒等时保持原生输入不受打断
308
+ if (formatValue !== rawValue) {
309
+ currentValue = rawValue
310
+ applyValue(formatValue)
311
+ } else {
312
+ currentValue = formatValue
313
+ updateHasValue(formatValue)
314
+ }
315
+ emitValue(formatValue)
274
316
  }
275
317
 
276
318
  // 输入框失去焦点时触发
277
319
  const onBlur = (event: any) => {
278
- const value = event && event.detail ? event.detail.value : ''
279
- emit('blur', value)
320
+ const detailValue = event && event.detail && event.detail.value !== undefined ? event.detail.value : currentValue
321
+ currentValue = toValueString(detailValue)
322
+ updateHasValue(currentValue)
323
+ emit('blur', currentValue)
280
324
  // H5端的blur会先于点击清除控件的点击click事件触发,导致focused
281
325
  // 瞬间为false,从而隐藏了清除控件而无法被点击到
282
326
  $uv.sleep(100).then(() => {
@@ -287,14 +331,20 @@
287
331
  }
288
332
 
289
333
  // 输入框聚焦时触发
290
- const onFocus = () => {
334
+ const onFocus = (event: any) => {
291
335
  focused.value = true
336
+ const detailValue = event && event.detail && event.detail.value !== undefined ? event.detail.value : currentValue
337
+ currentValue = toValueString(detailValue)
338
+ updateHasValue(currentValue)
292
339
  emit('focus')
293
340
  }
294
341
 
295
342
  // 点击完成按钮时触发
296
- const onConfirm = () => {
297
- emit('confirm', innerValue.value)
343
+ const onConfirm = (event: any) => {
344
+ const detailValue = event && event.detail && event.detail.value !== undefined ? event.detail.value : currentValue
345
+ currentValue = toValueString(detailValue)
346
+ updateHasValue(currentValue)
347
+ emit('confirm', currentValue)
298
348
  }
299
349
 
300
350
  // 键盘高度发生变化的时候触发此事件
@@ -302,22 +352,10 @@
302
352
  emit('keyboardheightchange', e)
303
353
  }
304
354
 
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
355
  // 点击清除控件
318
356
  const onClear = () => {
319
- innerValue.value = ''
320
- valueChange()
357
+ applyValue('')
358
+ emitValue('')
321
359
  nextTick(() => {
322
360
  emit('clear')
323
361
  })
@@ -16,6 +16,7 @@
16
16
  import { computed, onUnmounted, watch } from 'vue'
17
17
  import { $uv } from '../../utils'
18
18
  import props from './props'
19
+ import UvTransition from '../uv-transition/uv-transition.vue'
19
20
  import { preventEvent } from '../../utils/compat'
20
21
 
21
22
  defineOptions({
@@ -59,6 +59,11 @@
59
59
  import { computed, onMounted, onUnmounted, ref } from 'vue'
60
60
  import { $uv, platform } from '../../utils'
61
61
  import props from './props'
62
+ import UvIcon from '../uv-icon/uv-icon.vue'
63
+ import UvOverlay from '../uv-overlay/uv-overlay.vue'
64
+ import UvTransition from '../uv-transition/uv-transition.vue'
65
+ import UvSafeBottom from '../uv-safe-bottom/uv-safe-bottom.vue'
66
+ import UvStatusBar from '../uv-status-bar/uv-status-bar.vue'
62
67
  import { preventEvent } from '../../utils/compat'
63
68
 
64
69
  defineOptions({
@@ -80,6 +80,7 @@
80
80
  import { computed, getCurrentInstance, nextTick, onMounted, ref, watch } from 'vue'
81
81
  import { $uv } from '../../utils'
82
82
  import props from './props'
83
+ import UvBadge from '../uv-badge/uv-badge.vue'
83
84
  import { getRect, getRectNvue } from '../../utils/compat'
84
85
 
85
86
  defineOptions({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zfqh/uview-x",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
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": "abcec16d4c73e83cec02770a553cd89091e35cf3"
43
+ "gitHead": "2394e0c70de5af7326243f154b4eb92c65baa7f5"
44
44
  }