uview-plus 3.8.120 → 3.8.125
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 +55 -0
- package/components/u-icon/util.js +76 -32
- package/components/u-row-notice/u-row-notice.vue +5 -2
- package/components/u-signature/u-signature.vue +164 -103
- package/components/u-slider/props.js +2 -1
- package/components/u-slider/u-slider.vue +9 -3
- package/libs/function/throttle.js +6 -2
- package/libs/root/index.js +8 -86
- package/libs/vite/app-icon-font.js +93 -0
- package/libs/vite/index.js +50 -0
- package/libs/vite/utils.js +48 -0
- package/package.json +1 -1
- package/types/comps/slider.d.ts +15 -0
package/changelog.md
CHANGED
|
@@ -1,3 +1,48 @@
|
|
|
1
|
+
## 3.8.125
|
|
2
|
+
fix: 修复签名组件在 App 端无法绘制、笔迹画出后闪退的问题
|
|
3
|
+
|
|
4
|
+
u-signature 在 App 端(APP-PLUS 旧版 canvas)存在无法落笔、笔迹一闪即消失的问题,根因是画布上下文异步就绪未做握手、触摸坐标未换算到画布坐标系、以及每次 draw 都会刷新绘图命令队列导致已画笔迹被清空。
|
|
5
|
+
|
|
6
|
+
- 修复笔迹画出后立即消失:touchMove 改为「增量线段」绘制(beginPath + moveTo 上一点 + lineTo 当前点 + stroke),并使用 draw(true) 保留已绘制内容,不再每次 draw(false) 清空命令队列
|
|
7
|
+
- 修复 App 端坐标错乱、笔迹落到画布外:getCanvasPoint 优先使用画布相对坐标,缺失时用视口坐标减去画布位置进行换算
|
|
8
|
+
- 修复画布上下文异步就绪时首笔丢失:新增画布 ready / initCanvas 握手,缓存画布位置并在画布就绪后再响应绘制
|
|
9
|
+
- 收尾不再 closePath,避免手写笔迹出现回连直线;撤销回放逻辑保持兼容
|
|
10
|
+
- 覆盖 APP-PLUS / APP-HARMONY / APP-NVUE,微信小程序与 H5 行为保持不变
|
|
11
|
+
|
|
12
|
+
## 3.8.124
|
|
13
|
+
fix: App 端图标字体默认使用本地字体,新增通用 Vite 插件入口 UpVite
|
|
14
|
+
|
|
15
|
+
u-icon 的 useAppStaticIconFont 原先写死 false,只有启用 UniUpRoot 的项目才会在构建期被改成 true;未启用 Root 组件的 App 项目仍然走远程 CDN 字体,弱网或离线时图标会丢失。本地字体是随包能力,不该由是否使用 Root 组件决定。
|
|
16
|
+
|
|
17
|
+
- App / App-nvue 默认优先加载随包分发的 _www/static/app-plus/uview-plus/upicon.ttf
|
|
18
|
+
- App-Vue 在 uni.loadFontFace 失败后回退 config.iconUrl,每个页面只回退一次;App-nvue 因 dom.addRule 没有失败回调,注册前用 plus.io.resolveLocalFileSystemURL 探测字体文件,缺失时回退远程
|
|
19
|
+
- 新增通用 Vite 插件入口 UpVite(libs/vite/index.js),App 本地图标字体是它的第一个 feature;后续组件库新增构建期能力只需在插件内追加,业务项目不需要再改自己的 vite.config
|
|
20
|
+
- UniUpRoot 复用同一份图标字体 feature 并新增 appStaticIconFont: false 选项,只装 Root 插件的项目行为完全不变
|
|
21
|
+
- 不使用 Root 组件的项目单独引入 UpVite() 即可复制字体并移除 App 远程 @font-face
|
|
22
|
+
- 新增 verify:app-local-icon-font 回归校验,覆盖 static 路径、两端兜底、插件复制行为与关闭开关
|
|
23
|
+
- H5 与小程序行为不变,仍使用 config.iconUrl
|
|
24
|
+
|
|
25
|
+
## 3.8.123
|
|
26
|
+
fix: 修复 u-slider 区间模式 v-model:rangeValue 不生效 (#554)
|
|
27
|
+
|
|
28
|
+
u-slider 区间模式(isRange)拖动滑块时只抛出 update:modelValue,未抛出 update:rangeValue,导致 v-model:rangeValue 双向绑定无法更新。此外 rangeValue 的 default 为数组字面量,所有实例共用同一引用。
|
|
29
|
+
|
|
30
|
+
- 拖动结束时正确抛出 update:rangeValue 事件,同时兼容此前监听 update:modelValue 获取区间值的用法
|
|
31
|
+
- 值未变化时跳过事件抛出,避免父级对数组做拷贝或归一化时形成更新回环
|
|
32
|
+
- rangeValue 的 default 改为工厂函数,避免实例间共享数组引用
|
|
33
|
+
- 补充 isRange、rangeValue 及 onUpdate:rangeValue 类型声明
|
|
34
|
+
- 新增 verify:slider-range-value-model 回归校验脚本
|
|
35
|
+
|
|
36
|
+
## 3.8.122
|
|
37
|
+
fix: 修复 u-row-notice 横向滚动空格丢失
|
|
38
|
+
|
|
39
|
+
u-row-notice 每 20 个字符切分渲染到独立的 text 标签,若第 21 个字符为空格,会落在第二段行首被 CSS 折叠空白吃掉,导致横向滚动时两个字粘连。现将滚动文本 white-space 改为 pre,在保留原文空格的同时禁止换行,避免行首行尾空白被丢弃,并通过回归校验覆盖分段空格边界场景。
|
|
40
|
+
|
|
41
|
+
## 3.8.121
|
|
42
|
+
fix: 修复 up-button 真机首次点击后失效
|
|
43
|
+
|
|
44
|
+
默认 throttleTime 为 0 时直接同步执行点击回调,不再依赖异步 setTimeout 释放全局节流锁,避免真机定时器未释放时后续点击被拦截。新增零延迟连续调用回归校验。
|
|
45
|
+
|
|
1
46
|
## 3.8.120
|
|
2
47
|
fix(types): 组件类型可直接从包名导入,模板提示补齐 u-/u-- 前缀
|
|
3
48
|
|
|
@@ -2736,3 +2781,13 @@ fix: #261u-input在直接修改v-model的绑定值时,每隔一次会无法出
|
|
|
2736
2781
|
|
|
2737
2782
|
- feat: tabbar 新增多种风格预设、动态图标切换与轻量选中动画
|
|
2738
2783
|
- improvment: tabbar 补充高颜值本地图标示例并优化发布型底栏视觉
|
|
2784
|
+
## 3.8.121
|
|
2785
|
+
fix: 修复 up-button 真机首次点击后失效
|
|
2786
|
+
|
|
2787
|
+
用户反馈 up-button 在网页端正常,但在真机上点击一次后无法再次触发 click 事件。组件默认 throttleTime 为 0,原节流实现仍会先将全局节流标志置为 true,再依赖 setTimeout 回调异步释放;当真机运行环境没有按预期执行该定时回调时,全局标志会一直保持锁定状态,后续所有使用节流的点击都会被拦截。
|
|
2788
|
+
|
|
2789
|
+
- throttle 的 wait 小于等于 0 时直接同步执行回调并返回,不再挂起全局锁
|
|
2790
|
+
- 保留大于 0 时原有的立即节流与非立即节流行为,API 不变
|
|
2791
|
+
- 新增 `verify:button-throttle` 回归校验,覆盖零延迟连续调用场景
|
|
2792
|
+
|
|
2793
|
+
## 3.8.120
|
|
@@ -2,7 +2,10 @@ import config from '../../libs/config/config';
|
|
|
2
2
|
|
|
3
3
|
const iconFontFamily = 'uicon-iconfont';
|
|
4
4
|
const appIconFontUrl = '_www/static/app-plus/uview-plus/upicon.ttf';
|
|
5
|
-
|
|
5
|
+
// App 端默认优先加载随包分发的本地字体,图标不再依赖 CDN。
|
|
6
|
+
// 项目没有本地字体(未启用本地字体构建插件、或没拷贝 static/app-plus/uview-plus/upicon.ttf)时:
|
|
7
|
+
// App-Vue 在 uni.loadFontFace 失败后回退 config.iconUrl,App-nvue 先探测字体文件是否存在再决定字体地址。
|
|
8
|
+
const useAppStaticIconFont = true;
|
|
6
9
|
|
|
7
10
|
let params = {
|
|
8
11
|
loaded: false,
|
|
@@ -12,6 +15,8 @@ let params = {
|
|
|
12
15
|
// #ifdef APP-VUE
|
|
13
16
|
const appVueLoadedPages = new WeakSet();
|
|
14
17
|
const appVueLoadingPages = new WeakSet();
|
|
18
|
+
// 本地字体加载失败后回退远程字体,每个页面只回退一次,避免反复请求不存在的本地字体
|
|
19
|
+
const appVueFallbackPages = new WeakSet();
|
|
15
20
|
// #endif
|
|
16
21
|
|
|
17
22
|
const getCurrentAppVuePage = () => {
|
|
@@ -44,7 +49,6 @@ const getAppIconUrl = () => {
|
|
|
44
49
|
}
|
|
45
50
|
if (typeof plus !== 'undefined' && plus.io && typeof plus.io.convertLocalFileSystemURL === 'function') {
|
|
46
51
|
const iconFontPath = plus.io.convertLocalFileSystemURL(appIconFontUrl);
|
|
47
|
-
// if (iconFontPath) return iconFontPath;
|
|
48
52
|
return iconFontPath && iconFontPath.startsWith('file://') ? iconFontPath : 'file://' + iconFontPath;
|
|
49
53
|
}
|
|
50
54
|
return appIconFontUrl;
|
|
@@ -75,6 +79,69 @@ const markFontLoaded = () => {
|
|
|
75
79
|
}
|
|
76
80
|
};
|
|
77
81
|
|
|
82
|
+
// #ifdef APP-NVUE
|
|
83
|
+
const addNvueFontRule = (family, url) => {
|
|
84
|
+
const domModule = weex.requireModule('dom');
|
|
85
|
+
domModule.addRule('fontFace', {
|
|
86
|
+
'fontFamily': family,
|
|
87
|
+
'src': `url('${url}')`
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// nvue的dom.addRule没有失败回调,注册前先探测本地字体文件是否存在,不存在时回退远程字体
|
|
92
|
+
const registerNvueFontFace = (iconUrl) => {
|
|
93
|
+
const addFontFace = (url) => {
|
|
94
|
+
addNvueFontRule(iconFontFamily, url);
|
|
95
|
+
if (config.customIcon.family) {
|
|
96
|
+
addNvueFontRule(config.customIcon.family, config.customIcon.url);
|
|
97
|
+
}
|
|
98
|
+
markFontLoaded();
|
|
99
|
+
};
|
|
100
|
+
if (!useAppStaticIconFont || iconUrl === config.iconUrl) {
|
|
101
|
+
addFontFace(iconUrl);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (typeof plus === 'undefined' || !plus.io || typeof plus.io.resolveLocalFileSystemURL !== 'function') {
|
|
105
|
+
addFontFace(iconUrl);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const localFontPath = typeof plus.io.convertLocalFileSystemURL === 'function'
|
|
109
|
+
? plus.io.convertLocalFileSystemURL(appIconFontUrl)
|
|
110
|
+
: appIconFontUrl;
|
|
111
|
+
try {
|
|
112
|
+
plus.io.resolveLocalFileSystemURL(localFontPath, () => addFontFace(iconUrl), () => addFontFace(config.iconUrl));
|
|
113
|
+
} catch (e) {
|
|
114
|
+
addFontFace(iconUrl);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
// #endif
|
|
118
|
+
|
|
119
|
+
// #ifdef APP-VUE
|
|
120
|
+
const registerAppVueFontFace = (iconUrl, appVuePage) => {
|
|
121
|
+
uni.loadFontFace({
|
|
122
|
+
global: true, // 是否全局生效。微信小程序 '2.10.0'起支持全局生效,需在 app.vue 中调用。
|
|
123
|
+
family: iconFontFamily,
|
|
124
|
+
source: 'url("' + iconUrl + '")',
|
|
125
|
+
success() {
|
|
126
|
+
appVueLoadingPages.delete(appVuePage);
|
|
127
|
+
appVueLoadedPages.add(appVuePage);
|
|
128
|
+
markFontLoaded();
|
|
129
|
+
// console.log('内置字体图标加载成功');
|
|
130
|
+
},
|
|
131
|
+
fail() {
|
|
132
|
+
// 本地字体缺失时回退远程字体,保证未启用本地字体的老项目仍可显示图标
|
|
133
|
+
if (iconUrl !== config.iconUrl && !appVueFallbackPages.has(appVuePage)) {
|
|
134
|
+
appVueFallbackPages.add(appVuePage);
|
|
135
|
+
registerAppVueFontFace(config.iconUrl, appVuePage);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
appVueLoadingPages.delete(appVuePage);
|
|
139
|
+
// console.error('内置字体图标加载出错');
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
// #endif
|
|
144
|
+
|
|
78
145
|
// 加载字体方法
|
|
79
146
|
const loadFont = () => {
|
|
80
147
|
// #ifdef APP-VUE
|
|
@@ -94,42 +161,27 @@ const loadFont = () => {
|
|
|
94
161
|
// #ifdef APP-NVUE
|
|
95
162
|
// nvue通过weex的dom模块引入字体,相关文档地址如下:
|
|
96
163
|
// https://weex.apache.org/zh/docs/modules/dom.html#addrule
|
|
97
|
-
|
|
98
|
-
domModule.addRule('fontFace', {
|
|
99
|
-
'fontFamily': iconFontFamily,
|
|
100
|
-
'src': `url('${iconUrl}')`
|
|
101
|
-
});
|
|
102
|
-
if (config.customIcon.family) {
|
|
103
|
-
domModule.addRule('fontFace', {
|
|
104
|
-
'fontFamily': config.customIcon.family,
|
|
105
|
-
'src': `url('${config.customIcon.url}')`
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
markFontLoaded();
|
|
164
|
+
registerNvueFontFace(iconUrl);
|
|
109
165
|
// #endif
|
|
110
|
-
// #ifdef APP-VUE
|
|
166
|
+
// #ifdef APP-VUE
|
|
167
|
+
registerAppVueFontFace(iconUrl, appVuePage);
|
|
168
|
+
// #endif
|
|
169
|
+
// #ifdef H5 || MP-WEIXIN || MP-ALIPAY
|
|
111
170
|
uni.loadFontFace({
|
|
112
171
|
global: true, // 是否全局生效。微信小程序 '2.10.0'起支持全局生效,需在 app.vue 中调用。
|
|
113
172
|
family: iconFontFamily,
|
|
114
173
|
source: 'url("' + iconUrl + '")',
|
|
115
174
|
success() {
|
|
116
|
-
// #ifdef APP-VUE
|
|
117
|
-
appVueLoadingPages.delete(appVuePage);
|
|
118
|
-
appVueLoadedPages.add(appVuePage);
|
|
119
|
-
// #endif
|
|
120
175
|
markFontLoaded();
|
|
121
176
|
// console.log('内置字体图标加载成功');
|
|
122
177
|
},
|
|
123
178
|
fail() {
|
|
124
|
-
// #ifdef APP-VUE
|
|
125
|
-
appVueLoadingPages.delete(appVuePage);
|
|
126
|
-
// #endif
|
|
127
|
-
// #ifndef APP-VUE
|
|
128
179
|
params.loading = false;
|
|
129
|
-
// #endif
|
|
130
180
|
// console.error('内置字体图标加载出错');
|
|
131
181
|
}
|
|
132
182
|
});
|
|
183
|
+
// #endif
|
|
184
|
+
// #ifdef APP-VUE || H5 || MP-WEIXIN || MP-ALIPAY
|
|
133
185
|
if (config.customIcon.family) {
|
|
134
186
|
uni.loadFontFace({
|
|
135
187
|
global: true, // 是否全局生效。微信小程序 '2.10.0'起支持全局生效,需在 app.vue 中调用。
|
|
@@ -144,14 +196,6 @@ const loadFont = () => {
|
|
|
144
196
|
});
|
|
145
197
|
}
|
|
146
198
|
// #endif
|
|
147
|
-
// #ifdef APP-NVUE
|
|
148
|
-
// if (this.customFontFamily) {
|
|
149
|
-
// domModule.addRule('fontFace', {
|
|
150
|
-
// 'fontFamily': `${this.customPrefix}-${this.customFontFamily}`,
|
|
151
|
-
// 'src': `url('${this.customFontUrl}')`
|
|
152
|
-
// })
|
|
153
|
-
// }
|
|
154
|
-
// #endif
|
|
155
199
|
return true;
|
|
156
200
|
};
|
|
157
201
|
|
|
@@ -127,7 +127,9 @@
|
|
|
127
127
|
// 文字内容的样式
|
|
128
128
|
textStyle() {
|
|
129
129
|
let style = {}
|
|
130
|
-
|
|
130
|
+
// innerText把文本切分到多个text标签,切分点正好是空格时,nowrap会把它
|
|
131
|
+
// 当做行首/行尾空白丢弃,所以用pre保留原文空白,pre同样不会换行
|
|
132
|
+
style.whiteSpace = 'pre !important'
|
|
131
133
|
style.color = this.color
|
|
132
134
|
style.fontSize = addUnit(this.fontSize)
|
|
133
135
|
return style
|
|
@@ -312,7 +314,8 @@
|
|
|
312
314
|
// 这一句很重要,为了能让滚动左右连接起来
|
|
313
315
|
padding-left: 100%;
|
|
314
316
|
word-break: keep-all;
|
|
315
|
-
|
|
317
|
+
// pre而非nowrap:切分后的空格落在text首尾时不能被折叠掉
|
|
318
|
+
white-space: pre;
|
|
316
319
|
animation: u-loop-animation 10s linear infinite both;
|
|
317
320
|
/* #endif */
|
|
318
321
|
@include flex(row);
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
@touchstart="touchStart"
|
|
11
11
|
@touchmove="touchMove"
|
|
12
12
|
@touchend="touchEnd"
|
|
13
|
+
@ready="onCanvasReady"
|
|
13
14
|
:disable-scroll="true"
|
|
14
15
|
class="u-signature__canvas"
|
|
15
16
|
:style="{
|
|
@@ -75,6 +76,7 @@
|
|
|
75
76
|
|
|
76
77
|
<script>
|
|
77
78
|
import { t } from '../../libs/i18n'
|
|
79
|
+
import { upGetRect } from '../../libs/function/index.js'
|
|
78
80
|
export default {
|
|
79
81
|
name: 'u-signature',
|
|
80
82
|
props: {
|
|
@@ -132,9 +134,11 @@
|
|
|
132
134
|
],
|
|
133
135
|
showBrushSettings: false,
|
|
134
136
|
showColorSettings: false,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
lastPoint: null, // 保存上一个点的坐标
|
|
138
|
+
canvasInstance: null, // 缓存canvas实例
|
|
139
|
+
canvasReady: false, // up-canvas 是否已完成初始化(APP/NVUE 需异步)
|
|
140
|
+
canvasRect: { left: 0, top: 0 } // 画布在视口中的位置,用于坐标换算
|
|
141
|
+
}
|
|
138
142
|
},
|
|
139
143
|
computed: {
|
|
140
144
|
resolvedBgColor() {
|
|
@@ -150,10 +154,9 @@
|
|
|
150
154
|
}
|
|
151
155
|
},
|
|
152
156
|
mounted() {
|
|
153
|
-
//
|
|
157
|
+
// 等待 up-canvas 完成初始化(APP / NVUE 画布上下文需异步就绪)
|
|
154
158
|
this.$nextTick(() => {
|
|
155
|
-
this.
|
|
156
|
-
this.clearCanvas();
|
|
159
|
+
this.initCanvasInstance();
|
|
157
160
|
});
|
|
158
161
|
},
|
|
159
162
|
watch: {
|
|
@@ -192,110 +195,168 @@
|
|
|
192
195
|
},
|
|
193
196
|
|
|
194
197
|
// 获取签名画布实例
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
+
getCanvasInstance() {
|
|
199
|
+
if (this.canvasInstance) {
|
|
200
|
+
return this.canvasInstance;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const canvasRef = this.$refs.signatureCanvas;
|
|
204
|
+
if (canvasRef) {
|
|
205
|
+
this.canvasInstance = canvasRef;
|
|
206
|
+
// 实例已就绪则同步标记,避免首笔被 touchStart 拦截
|
|
207
|
+
if (canvasRef.ctx) {
|
|
208
|
+
this.canvasReady = true;
|
|
198
209
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
210
|
+
return canvasRef;
|
|
211
|
+
}
|
|
212
|
+
return null;
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
// 主动等待 up-canvas 完成初始化(部分平台画布上下文为异步)
|
|
216
|
+
async initCanvasInstance() {
|
|
217
|
+
const ref = this.getCanvasInstance();
|
|
218
|
+
if (!ref) return;
|
|
219
|
+
if (typeof ref.initCanvas === 'function') {
|
|
220
|
+
try {
|
|
221
|
+
const ok = await ref.initCanvas(true);
|
|
222
|
+
if (ok) {
|
|
223
|
+
this.onCanvasReady();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
} catch (e) {
|
|
227
|
+
// 忽略,等待 @ready 兜底
|
|
204
228
|
}
|
|
205
|
-
|
|
206
|
-
|
|
229
|
+
}
|
|
230
|
+
// 若 initCanvas 暂未就绪,up-canvas 的 @ready 事件会触发 onCanvasReady
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
// up-canvas 初始化完成回调(@ready 或 initCanvas 成功后触发)
|
|
234
|
+
onCanvasReady() {
|
|
235
|
+
if (this.canvasReady) return;
|
|
236
|
+
this.canvasReady = true;
|
|
237
|
+
this.getCanvasInstance();
|
|
238
|
+
this.refreshCanvasRect();
|
|
239
|
+
this.clearCanvas();
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
// 缓存画布在视口中的位置,用于将视口坐标换算为画布坐标
|
|
243
|
+
refreshCanvasRect() {
|
|
244
|
+
if (typeof upGetRect !== 'function') return;
|
|
245
|
+
upGetRect('#' + this.canvasId, false, this)
|
|
246
|
+
.then((rect) => {
|
|
247
|
+
if (rect && (rect.left || rect.top || rect.width)) {
|
|
248
|
+
this.canvasRect = {
|
|
249
|
+
left: rect.left || 0,
|
|
250
|
+
top: rect.top || 0
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
.catch(() => {});
|
|
255
|
+
},
|
|
207
256
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
257
|
+
touchStart(e) {
|
|
258
|
+
if (!this.canvasReady || !this.canvasInstance || !this.canvasInstance.ctx) {
|
|
259
|
+
// 画布尚未就绪:尝试初始化一次,待 @ready 后下一次触摸即可绘制
|
|
260
|
+
this.getCanvasInstance();
|
|
261
|
+
if (this.canvasInstance && typeof this.canvasInstance.initCanvas === 'function') {
|
|
262
|
+
this.canvasInstance.initCanvas(true)
|
|
263
|
+
.then(() => {
|
|
264
|
+
this.canvasReady = true;
|
|
265
|
+
this.refreshCanvasRect();
|
|
266
|
+
})
|
|
267
|
+
.catch(() => {});
|
|
211
268
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
this.isDrawing = true;
|
|
216
|
-
this.isEmpty = false;
|
|
217
|
-
this.currentPath = [];
|
|
218
|
-
|
|
219
|
-
const { x, y } = this.getCanvasPoint(e);
|
|
220
|
-
|
|
221
|
-
// 设置线条样式
|
|
222
|
-
this.canvasInstance.setLineStyle(this.lineColor, this.lineWidth);
|
|
223
|
-
|
|
224
|
-
// 开始路径
|
|
225
|
-
this.canvasInstance.beginPath();
|
|
226
|
-
this.canvasInstance.moveTo(x, y);
|
|
227
|
-
|
|
228
|
-
// 记录起始点
|
|
229
|
-
this.currentPath.push({
|
|
230
|
-
x,
|
|
231
|
-
y,
|
|
232
|
-
type: 'start',
|
|
233
|
-
color: this.lineColor,
|
|
234
|
-
width: this.lineWidth
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// 保存上一个点
|
|
238
|
-
this.lastPoint = { x, y };
|
|
239
|
-
|
|
240
|
-
// 阻止默认事件以提高性能
|
|
241
|
-
e.preventDefault();
|
|
242
|
-
},
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
243
271
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
// 阻止默认事件以提高性能
|
|
248
|
-
e.preventDefault();
|
|
249
|
-
|
|
250
|
-
const { x, y } = this.getCanvasPoint(e);
|
|
251
|
-
|
|
252
|
-
// 从上一个点画线到当前点
|
|
253
|
-
this.canvasInstance.lineTo(x, y);
|
|
254
|
-
this.canvasInstance.stroke(); // 实时绘制当前线段
|
|
255
|
-
this.currentPath.push({
|
|
256
|
-
x,
|
|
257
|
-
y,
|
|
258
|
-
type: 'move'
|
|
259
|
-
});
|
|
260
|
-
this.canvasInstance.draw(false);
|
|
261
|
-
|
|
262
|
-
// 更新上一个点
|
|
263
|
-
this.lastPoint = { x, y };
|
|
264
|
-
},
|
|
272
|
+
this.isDrawing = true;
|
|
273
|
+
this.isEmpty = false;
|
|
274
|
+
this.currentPath = [];
|
|
265
275
|
|
|
266
|
-
|
|
267
|
-
if (!this.isDrawing || !this.canvasInstance || !this.canvasInstance.ctx) return;
|
|
268
|
-
|
|
269
|
-
this.isDrawing = false;
|
|
270
|
-
this.canvasInstance.closePath();
|
|
271
|
-
this.lastPoint = null;
|
|
272
|
-
|
|
273
|
-
// 将当前路径加入栈中用于回退
|
|
274
|
-
if (this.currentPath.length > 0) {
|
|
275
|
-
this.pathStack.push([...this.currentPath]);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// 最后统一执行一次绘制
|
|
279
|
-
this.canvasInstance.draw(true);
|
|
280
|
-
},
|
|
276
|
+
const { x, y } = this.getCanvasPoint(e);
|
|
281
277
|
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
278
|
+
// 设置线条样式(每次起笔确保样式生效,兼容旧版 canvas 的 draw 后状态重置)
|
|
279
|
+
this.canvasInstance.setLineStyle(this.lineColor, this.lineWidth);
|
|
280
|
+
|
|
281
|
+
// 仅记录起点,真正的绘制在 touchMove 中以“增量线段”方式完成
|
|
282
|
+
this.lastPoint = { x, y };
|
|
283
|
+
this.currentPath.push({
|
|
284
|
+
x,
|
|
285
|
+
y,
|
|
286
|
+
type: 'start',
|
|
287
|
+
color: this.lineColor,
|
|
288
|
+
width: this.lineWidth
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
e.preventDefault();
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
touchMove(e) {
|
|
295
|
+
if (!this.isDrawing || !this.canvasInstance || !this.canvasInstance.ctx) return;
|
|
296
|
+
|
|
297
|
+
e.preventDefault();
|
|
298
|
+
|
|
299
|
+
const { x, y } = this.getCanvasPoint(e);
|
|
300
|
+
|
|
301
|
+
// 增量绘制:以上一个点为起点画到当前点。
|
|
302
|
+
// 旧版 canvas(APP-PLUS)每次 draw 会刷新命令队列,必须以“单段”方式绘制;
|
|
303
|
+
// 2D canvas 亦适用(draw 为 no-op,stroke 即时生效并保留已有像素)。
|
|
304
|
+
this.canvasInstance.setLineStyle(this.lineColor, this.lineWidth);
|
|
305
|
+
this.canvasInstance.beginPath();
|
|
306
|
+
this.canvasInstance.moveTo(this.lastPoint.x, this.lastPoint.y);
|
|
307
|
+
this.canvasInstance.lineTo(x, y);
|
|
308
|
+
this.canvasInstance.stroke();
|
|
309
|
+
this.canvasInstance.draw(true); // reserve:保留已绘制内容,避免笔迹闪退
|
|
310
|
+
|
|
311
|
+
this.currentPath.push({
|
|
312
|
+
x,
|
|
313
|
+
y,
|
|
314
|
+
type: 'move'
|
|
315
|
+
});
|
|
316
|
+
this.lastPoint = { x, y };
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
touchEnd(e) {
|
|
320
|
+
if (!this.isDrawing || !this.canvasInstance || !this.canvasInstance.ctx) return;
|
|
321
|
+
|
|
322
|
+
this.isDrawing = false;
|
|
323
|
+
this.lastPoint = null;
|
|
324
|
+
|
|
325
|
+
// 将当前路径加入栈中用于回退
|
|
326
|
+
if (this.currentPath.length > 0) {
|
|
327
|
+
this.pathStack.push([...this.currentPath]);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// 收尾绘制(保留已有内容)
|
|
331
|
+
this.canvasInstance.draw(true);
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
// 同步获取canvas坐标点(兼容各平台)
|
|
335
|
+
getCanvasPoint(e) {
|
|
336
|
+
const touch =
|
|
337
|
+
(e.touches && e.touches[0]) ||
|
|
338
|
+
(e.changedTouches && e.changedTouches[0]) ||
|
|
339
|
+
null;
|
|
340
|
+
if (!touch) return { x: 0, y: 0 };
|
|
341
|
+
|
|
342
|
+
// 2D canvas(MP/H5)与 NVUE webview 会在事件上直接挂载画布相对坐标
|
|
343
|
+
if (typeof touch.x === 'number' && typeof touch.y === 'number') {
|
|
344
|
+
return { x: touch.x, y: touch.y };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// 旧版 canvas(如 APP-PLUS)的 touch 仅提供视口坐标,需减去画布位置
|
|
348
|
+
const rect = this.canvasRect || { left: 0, top: 0 };
|
|
349
|
+
const clientX = touch.clientX !== undefined
|
|
350
|
+
? touch.clientX
|
|
351
|
+
: (touch.pageX !== undefined ? touch.pageX : 0);
|
|
352
|
+
const clientY = touch.clientY !== undefined
|
|
353
|
+
? touch.clientY
|
|
354
|
+
: (touch.pageY !== undefined ? touch.pageY : 0);
|
|
355
|
+
return {
|
|
356
|
+
x: clientX - rect.left,
|
|
357
|
+
y: clientY - rect.top
|
|
358
|
+
};
|
|
359
|
+
},
|
|
299
360
|
|
|
300
361
|
// 选择颜色
|
|
301
362
|
selectColor(color) {
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
export default {
|
|
127
127
|
name: 'u-slider',
|
|
128
128
|
mixins: [mpMixin, mixin, props],
|
|
129
|
-
emits: ["start", "changing", "change", "update:modelValue"],
|
|
129
|
+
emits: ["start", "changing", "change", "update:modelValue", "update:rangeValue"],
|
|
130
130
|
data() {
|
|
131
131
|
return {
|
|
132
132
|
startX: 0,
|
|
@@ -462,8 +462,14 @@
|
|
|
462
462
|
}
|
|
463
463
|
// 修改value值
|
|
464
464
|
if (this.isRange) {
|
|
465
|
-
|
|
466
|
-
|
|
465
|
+
// 值没有变化时不再抛出事件,避免父级对数组做拷贝或归一化时形成更新回环
|
|
466
|
+
if (this.rangeValue[index] !== valueFormat) {
|
|
467
|
+
this.rangeValue[index] = valueFormat;
|
|
468
|
+
// 区间模式由rangeValue双向绑定,必须抛出update:rangeValue,v-model:rangeValue才能生效
|
|
469
|
+
this.$emit("update:rangeValue", this.rangeValue);
|
|
470
|
+
// 兼容此前监听update:modelValue获取区间值的用法
|
|
471
|
+
this.$emit("update:modelValue", this.rangeValue);
|
|
472
|
+
}
|
|
467
473
|
} else {
|
|
468
474
|
// #ifdef VUE3
|
|
469
475
|
this.$emit("update:modelValue", valueFormat);
|
|
@@ -8,8 +8,12 @@ let flag;
|
|
|
8
8
|
* @param {Boolean} immediate 是否立即执行
|
|
9
9
|
* @return null
|
|
10
10
|
*/
|
|
11
|
-
export function throttle(func, wait = 500, immediate = true) {
|
|
12
|
-
if (
|
|
11
|
+
export function throttle(func, wait = 500, immediate = true) {
|
|
12
|
+
if (wait <= 0) {
|
|
13
|
+
typeof func === 'function' && func()
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
if (immediate) {
|
|
13
17
|
if (!flag) {
|
|
14
18
|
flag = true
|
|
15
19
|
// 如果是立即执行,则在wait毫秒内开始时执行
|
package/libs/root/index.js
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
import { dirname, relative, resolve } from 'node:path'
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import process from 'node:process'
|
|
4
|
-
import {
|
|
4
|
+
import { existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs'
|
|
5
5
|
|
|
6
6
|
import { createFilter, normalizePath } from 'vite'
|
|
7
7
|
|
|
8
8
|
import { transformNvuePage, transformPage } from './page.js'
|
|
9
9
|
import { rebuildUpApp, registerUpApp } from './root.js'
|
|
10
10
|
import { loadPagesJson, normalizePlatformPath, toArray } from './utils.js'
|
|
11
|
+
import { createAppIconFontFeature } from '../vite/app-icon-font.js'
|
|
12
|
+
import { detectProjectRoot } from '../vite/utils.js'
|
|
11
13
|
|
|
12
14
|
const rootLibPath = normalizePath(dirname(fileURLToPath(import.meta.url)))
|
|
13
|
-
const appStaticIconFontRelativePath = 'static/app-plus/uview-plus/upicon.ttf'
|
|
14
|
-
const appStaticIconFontFlag = 'const useAppStaticIconFont = false;'
|
|
15
|
-
const appStaticIconFontEnabledFlag = 'const useAppStaticIconFont = true;'
|
|
16
|
-
const appRemoteIconFontCondition = /\/\*\s*#ifdef\s+APP\s+\|\|\s+(MP-QQ\s+\|\|\s+MP-TOUTIAO\s+\|\|\s+MP-BAIDU\s+\|\|\s+MP-KUAISHOU\s+\|\|\s+MP-XHS)\s*\*\//
|
|
17
|
-
const appCompiledRemoteIconFontFace = /[ \t]*(?:\/\/[^\n]*\n[ \t]*)?@font-face\s*\{[^{}]*font-family:\s*['"]?uicon-iconfont['"]?;?[^{}]*at\.alicdn\.com\/t\/font_2225171[^{}]*\}\s*/
|
|
18
|
-
const appCompiledRemoteIconFontFaceGlobal = /[ \t]*(?:\/\/[^\n]*\n[ \t]*)?@font-face\s*\{[^{}]*font-family:\s*['"]?uicon-iconfont['"]?;?[^{}]*at\.alicdn\.com\/t\/font_2225171[^{}]*\}\s*/g
|
|
19
15
|
|
|
20
16
|
export default function UniUpRoot(options = {}) {
|
|
21
17
|
const rootOptions = {
|
|
@@ -28,41 +24,12 @@ export default function UniUpRoot(options = {}) {
|
|
|
28
24
|
}
|
|
29
25
|
const rootFileName = String(rootOptions.rootFileName || 'App.up').replace(/\.vue$/i, '')
|
|
30
26
|
|
|
31
|
-
const detectProjectRoot = () => {
|
|
32
|
-
const cwd = normalizePath(process.env.INIT_CWD || process.cwd())
|
|
33
|
-
const envInputDir = process.env.UNI_INPUT_DIR ? normalizePath(process.env.UNI_INPUT_DIR) : ''
|
|
34
|
-
const cliRootPath = normalizePath(resolve(cwd, 'src'))
|
|
35
|
-
const hbuilderRootPath = cwd
|
|
36
|
-
|
|
37
|
-
const isPageRoot = (path) => existsSync(resolve(path, 'pages.json'))
|
|
38
|
-
|
|
39
|
-
if (envInputDir && isPageRoot(envInputDir)) {
|
|
40
|
-
const projectType = envInputDir.endsWith('/src') ? 'cli' : 'hbuilder'
|
|
41
|
-
return { rootPath: envInputDir, projectType }
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (isPageRoot(cliRootPath)) {
|
|
45
|
-
return { rootPath: cliRootPath, projectType: 'cli' }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (isPageRoot(hbuilderRootPath)) {
|
|
49
|
-
return { rootPath: hbuilderRootPath, projectType: 'hbuilder' }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
rootPath: envInputDir || cliRootPath,
|
|
54
|
-
projectType: envInputDir && !envInputDir.endsWith('/src') ? 'hbuilder' : 'cli',
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
27
|
const projectInfo = detectProjectRoot()
|
|
59
28
|
const rootPath = normalizePath(projectInfo.rootPath)
|
|
29
|
+
// App 本地图标字体与 Root 组件无关,这里复用 UniUp 的同一个 feature,保持老项目行为不变
|
|
30
|
+
const appIconFont = createAppIconFontFeature({ enabled: rootOptions.appStaticIconFont !== false })
|
|
60
31
|
const appUpPath = normalizePath(resolve(rootPath, `${rootFileName}.vue`))
|
|
61
32
|
const rootToastHostPath = normalizePath(resolve(rootLibPath, 'root-toast-host.vue'))
|
|
62
|
-
const iconFontSourcePath = normalizePath(resolve(rootLibPath, '../../components/u-icon/upicon.ttf'))
|
|
63
|
-
const appStaticIconFontPath = normalizePath(resolve(rootPath, appStaticIconFontRelativePath))
|
|
64
|
-
const uIconUtilPath = normalizePath(resolve(rootPath, 'uni_modules/uview-plus/components/u-icon/util.js'))
|
|
65
|
-
const uIconVuePath = normalizePath(resolve(rootPath, 'uni_modules/uview-plus/components/u-icon/u-icon.vue'))
|
|
66
33
|
const themeRuntimePath = normalizePath(resolve(rootPath, 'uni_modules/uview-plus/libs/theme/runtime.js'))
|
|
67
34
|
const pagesPath = normalizePath(resolve(rootPath, 'pages.json'))
|
|
68
35
|
const excludedPaths = toArray(rootOptions.excludePages)
|
|
@@ -99,51 +66,6 @@ export default function UniUpRoot(options = {}) {
|
|
|
99
66
|
writeFileSync(appUpPath, defaultRootSfc, 'utf-8')
|
|
100
67
|
}
|
|
101
68
|
|
|
102
|
-
const shouldCopyFile = (sourcePath, targetPath) => {
|
|
103
|
-
if (!existsSync(targetPath)) return true
|
|
104
|
-
if (statSync(sourcePath).size !== statSync(targetPath).size) return true
|
|
105
|
-
return !readFileSync(sourcePath).equals(readFileSync(targetPath))
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const ensureAppStaticIconFont = () => {
|
|
109
|
-
if (process.env.UNI_PLATFORM !== 'app') return
|
|
110
|
-
if (!existsSync(iconFontSourcePath)) {
|
|
111
|
-
throw new Error(`uview-plus built-in icon font is missing: ${iconFontSourcePath}`)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
mkdirSync(dirname(appStaticIconFontPath), { recursive: true })
|
|
115
|
-
if (shouldCopyFile(iconFontSourcePath, appStaticIconFontPath)) {
|
|
116
|
-
copyFileSync(iconFontSourcePath, appStaticIconFontPath)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const removeAppCompiledRemoteIconFontFace = (code) => code.replace(appCompiledRemoteIconFontFaceGlobal, '')
|
|
121
|
-
|
|
122
|
-
const transformAppStaticIconFont = (code, cleanId) => {
|
|
123
|
-
if (process.env.UNI_PLATFORM !== 'app') return null
|
|
124
|
-
if (cleanId === uIconUtilPath && code.includes(appStaticIconFontFlag)) {
|
|
125
|
-
return code.replace(appStaticIconFontFlag, appStaticIconFontEnabledFlag)
|
|
126
|
-
}
|
|
127
|
-
if (cleanId === uIconVuePath && appRemoteIconFontCondition.test(code)) {
|
|
128
|
-
return code.replace(appRemoteIconFontCondition, '/* #ifdef $1 */')
|
|
129
|
-
}
|
|
130
|
-
if (cleanId === uIconVuePath && appCompiledRemoteIconFontFace.test(code)) {
|
|
131
|
-
return removeAppCompiledRemoteIconFontFace(code)
|
|
132
|
-
}
|
|
133
|
-
return null
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const removeAppRemoteIconFontFaceFromCssAssets = (bundle) => {
|
|
137
|
-
if (process.env.UNI_PLATFORM !== 'app') return
|
|
138
|
-
Object.values(bundle).forEach((asset) => {
|
|
139
|
-
if (asset.type !== 'asset') return
|
|
140
|
-
if (!asset.fileName.endsWith('.css')) return
|
|
141
|
-
if (typeof asset.source !== 'string') return
|
|
142
|
-
if (!appCompiledRemoteIconFontFace.test(asset.source)) return
|
|
143
|
-
asset.source = removeAppCompiledRemoteIconFontFace(asset.source)
|
|
144
|
-
})
|
|
145
|
-
}
|
|
146
|
-
|
|
147
69
|
const refreshPagesJson = () => {
|
|
148
70
|
if (!existsSync(pagesPath)) return
|
|
149
71
|
const mtimeMs = statSync(pagesPath).mtimeMs
|
|
@@ -164,7 +86,7 @@ export default function UniUpRoot(options = {}) {
|
|
|
164
86
|
}
|
|
165
87
|
},
|
|
166
88
|
buildStart() {
|
|
167
|
-
|
|
89
|
+
appIconFont.ensure()
|
|
168
90
|
ensureRootFile()
|
|
169
91
|
refreshPagesJson()
|
|
170
92
|
},
|
|
@@ -173,7 +95,7 @@ export default function UniUpRoot(options = {}) {
|
|
|
173
95
|
const isSfcBlock = id.includes('?')
|
|
174
96
|
const cleanId = normalizePath(id.split('?')[0])
|
|
175
97
|
|
|
176
|
-
const iconFontCode =
|
|
98
|
+
const iconFontCode = appIconFont.transform(code, cleanId)
|
|
177
99
|
if (iconFontCode) {
|
|
178
100
|
return {
|
|
179
101
|
code: iconFontCode,
|
|
@@ -218,7 +140,7 @@ export default function UniUpRoot(options = {}) {
|
|
|
218
140
|
return null
|
|
219
141
|
},
|
|
220
142
|
generateBundle(_, bundle) {
|
|
221
|
-
|
|
143
|
+
appIconFont.generateBundle(bundle)
|
|
222
144
|
},
|
|
223
145
|
}
|
|
224
146
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, statSync } from 'node:fs'
|
|
4
|
+
|
|
5
|
+
import { detectProjectRoot, isAppPlatform, normalizePath, toCleanId } from './utils.js'
|
|
6
|
+
|
|
7
|
+
const featurePath = normalizePath(dirname(fileURLToPath(import.meta.url)))
|
|
8
|
+
export const appStaticIconFontRelativePath = 'static/app-plus/uview-plus/upicon.ttf'
|
|
9
|
+
// 源码默认 App 端优先本地字体,只有在显式关闭本地字体时才切回 config.iconUrl
|
|
10
|
+
// 不带分号,兼容有/无分号两种代码风格
|
|
11
|
+
const appStaticIconFontFlag = 'const useAppStaticIconFont = true'
|
|
12
|
+
const appStaticIconFontDisabledFlag = 'const useAppStaticIconFont = false'
|
|
13
|
+
const appRemoteIconFontCondition = /\/\*\s*#ifdef\s+APP\s+\|\|\s+(MP-QQ\s+\|\|\s+MP-TOUTIAO\s+\|\|\s+MP-BAIDU\s+\|\|\s+MP-KUAISHOU\s+\|\|\s+MP-XHS)\s*\*\//
|
|
14
|
+
const appCompiledRemoteIconFontFace = /[ \t]*(?:\/\/[^\n]*\n[ \t]*)?@font-face\s*\{[^{}]*font-family:\s*['"]?uicon-iconfont['"]?;?[^{}]*at\.alicdn\.com\/t\/font_2225171[^{}]*\}\s*/
|
|
15
|
+
const appCompiledRemoteIconFontFaceGlobal = /[ \t]*(?:\/\/[^\n]*\n[ \t]*)?@font-face\s*\{[^{}]*font-family:\s*['"]?uicon-iconfont['"]?;?[^{}]*at\.alicdn\.com\/t\/font_2225171[^{}]*\}\s*/g
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* App 本地图标字体能力(UniUp 的一个 feature)。
|
|
19
|
+
*
|
|
20
|
+
* 与 Root 组件无关:App / App-nvue 默认优先加载随包分发的 static 字体,
|
|
21
|
+
* 但字体文件必须由构建插件复制到应用 static 目录,本 feature 负责这件事,
|
|
22
|
+
* 并在 App 构建时移除远程 CSS 字体,避免本地字体与远程字体同时加载。
|
|
23
|
+
*/
|
|
24
|
+
export function createAppIconFontFeature(options = {}) {
|
|
25
|
+
const enabled = options.enabled !== false
|
|
26
|
+
const projectInfo = detectProjectRoot()
|
|
27
|
+
const rootPath = normalizePath(projectInfo.rootPath)
|
|
28
|
+
const iconFontSourcePath = normalizePath(resolve(featurePath, '../../components/u-icon/upicon.ttf'))
|
|
29
|
+
const appStaticIconFontPath = normalizePath(resolve(rootPath, appStaticIconFontRelativePath))
|
|
30
|
+
const uIconUtilPath = normalizePath(resolve(rootPath, 'uni_modules/uview-plus/components/u-icon/util.js'))
|
|
31
|
+
const uIconVuePath = normalizePath(resolve(rootPath, 'uni_modules/uview-plus/components/u-icon/u-icon.vue'))
|
|
32
|
+
|
|
33
|
+
const shouldCopyFile = (sourcePath, targetPath) => {
|
|
34
|
+
if (!existsSync(targetPath)) return true
|
|
35
|
+
if (statSync(sourcePath).size !== statSync(targetPath).size) return true
|
|
36
|
+
return !readFileSync(sourcePath).equals(readFileSync(targetPath))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const removeAppCompiledRemoteIconFontFace = (code) => code.replace(appCompiledRemoteIconFontFaceGlobal, '')
|
|
40
|
+
|
|
41
|
+
const ensure = () => {
|
|
42
|
+
if (!enabled) return
|
|
43
|
+
if (!isAppPlatform()) return
|
|
44
|
+
if (!existsSync(iconFontSourcePath)) {
|
|
45
|
+
throw new Error(`uview-plus built-in icon font is missing: ${iconFontSourcePath}`)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
mkdirSync(dirname(appStaticIconFontPath), { recursive: true })
|
|
49
|
+
if (shouldCopyFile(iconFontSourcePath, appStaticIconFontPath)) {
|
|
50
|
+
copyFileSync(iconFontSourcePath, appStaticIconFontPath)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const transform = (code, id) => {
|
|
55
|
+
if (!isAppPlatform()) return null
|
|
56
|
+
const cleanId = toCleanId(id)
|
|
57
|
+
if (!enabled) {
|
|
58
|
+
if (cleanId === uIconUtilPath && code.includes(appStaticIconFontFlag)) {
|
|
59
|
+
return code.replace(appStaticIconFontFlag, appStaticIconFontDisabledFlag)
|
|
60
|
+
}
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
if (cleanId === uIconVuePath && appRemoteIconFontCondition.test(code)) {
|
|
64
|
+
return code.replace(appRemoteIconFontCondition, '/* #ifdef $1 */')
|
|
65
|
+
}
|
|
66
|
+
if (cleanId === uIconVuePath && appCompiledRemoteIconFontFace.test(code)) {
|
|
67
|
+
return removeAppCompiledRemoteIconFontFace(code)
|
|
68
|
+
}
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const generateBundle = (bundle) => {
|
|
73
|
+
if (!enabled) return
|
|
74
|
+
if (!isAppPlatform()) return
|
|
75
|
+
Object.values(bundle).forEach((asset) => {
|
|
76
|
+
if (asset.type !== 'asset') return
|
|
77
|
+
if (!asset.fileName.endsWith('.css')) return
|
|
78
|
+
if (typeof asset.source !== 'string') return
|
|
79
|
+
if (!appCompiledRemoteIconFontFace.test(asset.source)) return
|
|
80
|
+
asset.source = removeAppCompiledRemoteIconFontFace(asset.source)
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
name: 'app-icon-font',
|
|
86
|
+
enabled,
|
|
87
|
+
rootPath,
|
|
88
|
+
appStaticIconFontPath,
|
|
89
|
+
ensure,
|
|
90
|
+
transform,
|
|
91
|
+
generateBundle,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createAppIconFontFeature } from './app-icon-font.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* uview-plus 通用 Vite 插件(统一入口)。
|
|
5
|
+
*
|
|
6
|
+
* 设计约定:
|
|
7
|
+
* - 用户只需要在 vite.config 里注册一次 `UpVite()`,之后组件库新增任何构建期能力
|
|
8
|
+
* 都在这里追加 feature,用户不需要改自己的配置。
|
|
9
|
+
* - 每个 feature 是一个对象,按需实现以下钩子(都可选):
|
|
10
|
+
* { name, ensure(), transform(code, id), generateBundle(bundle) }
|
|
11
|
+
* `ensure()` 对应 buildStart,`transform` 返回改写后的源码字符串或 null。
|
|
12
|
+
*
|
|
13
|
+
* 用法:
|
|
14
|
+
* import UpVite from 'uview-plus/libs/vite/index.js'
|
|
15
|
+
* export default defineConfig({ plugins: [UpVite(), uni()] })
|
|
16
|
+
*/
|
|
17
|
+
export function createUpViteFeatures(options = {}) {
|
|
18
|
+
return [
|
|
19
|
+
// App 端本地图标字体:把内置 upicon.ttf 复制到 static 并移除 App 远程 @font-face
|
|
20
|
+
createAppIconFontFeature({ enabled: options.appStaticIconFont !== false }),
|
|
21
|
+
// 后续新增构建期能力在此追加,保持对用户的配置零改动
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default function UpVite(options = {}) {
|
|
26
|
+
const features = createUpViteFeatures(options)
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
name: 'vite-plugin-up-vite',
|
|
30
|
+
enforce: 'pre',
|
|
31
|
+
buildStart() {
|
|
32
|
+
features.forEach(feature => feature.ensure?.())
|
|
33
|
+
},
|
|
34
|
+
transform(code, id) {
|
|
35
|
+
for (const feature of features) {
|
|
36
|
+
const transformed = feature.transform?.(code, id)
|
|
37
|
+
if (transformed) {
|
|
38
|
+
return {
|
|
39
|
+
code: transformed,
|
|
40
|
+
map: null,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null
|
|
45
|
+
},
|
|
46
|
+
generateBundle(_, bundle) {
|
|
47
|
+
features.forEach(feature => feature.generateBundle?.(bundle))
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 统一为正斜杠路径。
|
|
7
|
+
*
|
|
8
|
+
* 这里刻意不 import vite 的 normalizePath —— 让 libs/vite 目录保持自包含,
|
|
9
|
+
* 宿主项目不一定装了 vite 包(HBuilderX 项目走 HBuilderX 内置构建)。
|
|
10
|
+
*/
|
|
11
|
+
export const normalizePath = (path) => {
|
|
12
|
+
if (!path) return ''
|
|
13
|
+
return String(path).replace(/\\/g, '/')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 识别宿主项目根目录,同时兼容 CLI 项目与 HBuilderX 项目。
|
|
18
|
+
*/
|
|
19
|
+
export function detectProjectRoot() {
|
|
20
|
+
const cwd = normalizePath(process.env.INIT_CWD || process.cwd())
|
|
21
|
+
const envInputDir = process.env.UNI_INPUT_DIR ? normalizePath(process.env.UNI_INPUT_DIR) : ''
|
|
22
|
+
const cliRootPath = normalizePath(resolve(cwd, 'src'))
|
|
23
|
+
const hbuilderRootPath = cwd
|
|
24
|
+
|
|
25
|
+
const isPageRoot = (path) => existsSync(resolve(path, 'pages.json'))
|
|
26
|
+
|
|
27
|
+
if (envInputDir && isPageRoot(envInputDir)) {
|
|
28
|
+
const projectType = envInputDir.endsWith('/src') ? 'cli' : 'hbuilder'
|
|
29
|
+
return { rootPath: envInputDir, projectType }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (isPageRoot(cliRootPath)) {
|
|
33
|
+
return { rootPath: cliRootPath, projectType: 'cli' }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (isPageRoot(hbuilderRootPath)) {
|
|
37
|
+
return { rootPath: hbuilderRootPath, projectType: 'hbuilder' }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
rootPath: envInputDir || cliRootPath,
|
|
42
|
+
projectType: envInputDir && !envInputDir.endsWith('/src') ? 'hbuilder' : 'cli',
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const isAppPlatform = () => process.env.UNI_PLATFORM === 'app'
|
|
47
|
+
|
|
48
|
+
export const toCleanId = (id) => normalizePath(String(id).split('?')[0])
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "uview-plus",
|
|
3
3
|
"name": "uview-plus",
|
|
4
4
|
"displayName": "零云®uview-plus3.0重磅发布,全面的Vue3鸿蒙跨端移动组件库,组件丰富维护更新稳定。",
|
|
5
|
-
"version": "3.8.
|
|
5
|
+
"version": "3.8.125",
|
|
6
6
|
"description": "零云®uview-plus已兼容vue3支持多语言,120+全面的组件和便捷的工具会让您信手拈来。近期新增拖动排序、条码、图片裁剪、下拉刷新、虚拟列表、签名、Markdown等。",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"uview",
|
package/types/comps/slider.d.ts
CHANGED
|
@@ -51,6 +51,21 @@ declare interface SliderProps {
|
|
|
51
51
|
* 滑块按钮自定义样式
|
|
52
52
|
*/
|
|
53
53
|
blockStyle?: unknown
|
|
54
|
+
/**
|
|
55
|
+
* 是否区间模式(双滑块)
|
|
56
|
+
* @default false
|
|
57
|
+
*/
|
|
58
|
+
isRange?: boolean
|
|
59
|
+
/**
|
|
60
|
+
* 区间模式下的取值,支持 v-model:rangeValue 双向绑定
|
|
61
|
+
* @default [0, 0]
|
|
62
|
+
*/
|
|
63
|
+
rangeValue?: (string | number)[]
|
|
64
|
+
/**
|
|
65
|
+
* 区间模式下更新 v-model:rangeValue
|
|
66
|
+
* @param value 当前区间值
|
|
67
|
+
*/
|
|
68
|
+
"onUpdate:rangeValue"?: (value: (string | number)[]) => any
|
|
54
69
|
/**
|
|
55
70
|
* 更新v-model的
|
|
56
71
|
* @param value 当前值
|