uview-plus 3.8.86 → 3.8.106
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 +150 -3
- package/components/u-action-sheet/u-action-sheet.vue +21 -2
- package/components/u-barcode/u-barcode.vue +0 -2
- package/components/u-button/u-button.vue +2 -5
- package/components/u-canvas/u-canvas.vue +368 -74
- package/components/u-cell/u-cell.vue +8 -0
- package/components/u-datetime-picker/u-datetime-picker.vue +53 -4
- package/components/u-dragsort/u-dragsort.vue +55 -31
- package/components/u-icon/u-icon.vue +12 -3
- package/components/u-icon/util.js +81 -10
- package/components/u-index-item/u-index-item.vue +1 -1
- package/components/u-index-list/u-index-list.vue +1 -1
- package/components/u-novel-reader/content-normalizer.js +80 -0
- package/components/u-novel-reader/layout-engine.js +225 -0
- package/components/u-novel-reader/measure-adapter.js +69 -0
- package/components/u-novel-reader/novelReader.js +35 -0
- package/components/u-novel-reader/persistence.js +144 -0
- package/components/u-novel-reader/props.js +100 -0
- package/components/u-novel-reader/reader-catalog.vue +234 -0
- package/components/u-novel-reader/reader-content.vue +226 -0
- package/components/u-novel-reader/reader-core.js +151 -0
- package/components/u-novel-reader/reader-settings.vue +325 -0
- package/components/u-novel-reader/reader-toolbar.vue +313 -0
- package/components/u-novel-reader/theme-vars.scss +49 -0
- package/components/u-novel-reader/u-novel-reader.vue +836 -0
- package/components/u-poster/u-poster.vue +236 -219
- package/components/u-qrcode/qrcode.js +56 -49
- package/components/u-qrcode/u-qrcode.vue +159 -88
- package/components/u-scroll-list/scrollWxs.wxs +1 -1
- package/components/u-slider/u-slider.vue +1 -1
- package/components/u-swipe-action-item/index.wxs +43 -25
- package/components/u-swipe-action-item/nvue.js +9 -0
- package/components/u-swipe-action-item/other.js +32 -21
- package/components/u-swipe-action-item/props.js +5 -0
- package/components/u-swipe-action-item/swipeActionItem.js +1 -0
- package/components/u-swipe-action-item/u-swipe-action-item.vue +28 -4
- package/components/u-switch/u-switch.vue +1 -1
- package/components/u-tabbar/u-tabbar.vue +2 -1
- package/components/u-tabbar-item/u-tabbar-item.vue +64 -21
- package/components/u-tabs/u-tabs.vue +11 -8
- package/components/u-tabs-pro/u-tabs-pro.vue +212 -0
- package/components/u-text/text.js +1 -1
- package/components/u-text/u-text.vue +18 -6
- package/components/u-upload/u-upload.vue +1 -1
- package/components/u-waterfall/u-waterfall.vue +118 -65
- package/libs/config/config.js +1 -1
- package/libs/config/props.js +1 -0
- package/libs/function/index.js +66 -0
- package/libs/mixin/mixin.js +4 -50
- package/libs/root/index.js +78 -5
- package/libs/root/page.js +7 -7
- package/libs/root/root.js +73 -3
- package/libs/util/app-nvue-webview-canvas.js +486 -0
- package/libs/util/gcanvas/bridge/bridge-weex.js +0 -2
- package/libs/util/gcanvas/context-2d/FillStyleLinearGradient.js +0 -0
- package/libs/util/gcanvas/context-2d/FillStylePattern.js +0 -0
- package/libs/util/gcanvas/context-2d/FillStyleRadialGradient.js +0 -0
- package/libs/util/gcanvas/context-2d/RenderingContext.js +0 -0
- package/libs/util/gcanvas/context-webgl/ActiveInfo.js +0 -0
- package/libs/util/gcanvas/context-webgl/Buffer.js +0 -0
- package/libs/util/gcanvas/context-webgl/Framebuffer.js +0 -0
- package/libs/util/gcanvas/context-webgl/GLenum.js +0 -0
- package/libs/util/gcanvas/context-webgl/GLmethod.js +0 -0
- package/libs/util/gcanvas/context-webgl/GLtype.js +0 -0
- package/libs/util/gcanvas/context-webgl/Program.js +0 -0
- package/libs/util/gcanvas/context-webgl/Renderbuffer.js +0 -0
- package/libs/util/gcanvas/context-webgl/RenderingContext.js +0 -0
- package/libs/util/gcanvas/context-webgl/Shader.js +0 -0
- package/libs/util/gcanvas/context-webgl/ShaderPrecisionFormat.js +0 -0
- package/libs/util/gcanvas/context-webgl/Texture.js +0 -0
- package/libs/util/gcanvas/context-webgl/UniformLocation.js +0 -0
- package/libs/util/gcanvas/context-webgl/classUtils.js +0 -0
- package/libs/util/gcanvas/env/canvas.js +0 -0
- package/libs/util/gcanvas/env/image.js +0 -0
- package/libs/util/gcanvas/env/tool.js +0 -0
- package/libs/util/gcanvas/index.js +3 -3
- package/package.json +1 -1
- package/types/comps/swipeActionItem.d.ts +13 -0
- package/types/comps/tabs.d.ts +2 -1
- package/types/comps/text.d.ts +1 -1
- package/types/func.d.ts +23 -0
- package/types/index.d.ts +1 -0
|
@@ -108,7 +108,10 @@
|
|
|
108
108
|
showByClickInput: false, // 是否在hasInput模式下显示日期选择弹唱
|
|
109
109
|
columns: [],
|
|
110
110
|
innerDefaultIndex: [],
|
|
111
|
-
innerFormatter: (type, value) => value
|
|
111
|
+
innerFormatter: (type, value) => value,
|
|
112
|
+
// 记录最近一次通过change上抛给外部的值,用于去重,
|
|
113
|
+
// 避免边界(min/max)变化导致程序化重建列时再次触发change
|
|
114
|
+
lastEmitValue: null
|
|
112
115
|
}
|
|
113
116
|
},
|
|
114
117
|
watch: {
|
|
@@ -124,6 +127,8 @@
|
|
|
124
127
|
this.innerValue = this.correctValue(this.value)
|
|
125
128
|
// #endif
|
|
126
129
|
this.updateColumnValue(this.innerValue)
|
|
130
|
+
// 打开时以外部值为基准,重置去重基准
|
|
131
|
+
this.lastEmitValue = this.innerValue
|
|
127
132
|
}
|
|
128
133
|
},
|
|
129
134
|
// #ifdef VUE3
|
|
@@ -138,14 +143,20 @@
|
|
|
138
143
|
// this.getInputValue(newValue)
|
|
139
144
|
},
|
|
140
145
|
// #endif
|
|
141
|
-
|
|
146
|
+
// mode变化会改变已选值的语义(时间串<->时间戳),需要完整重新初始化
|
|
147
|
+
mode() {
|
|
142
148
|
this.init()
|
|
149
|
+
},
|
|
150
|
+
propsChange() {
|
|
151
|
+
// 边界(min/max、filter)变化时,保留当前已选值,仅重新校正并重建各列,
|
|
152
|
+
// 不回退到外部 modelValue,避免未确认前丢失用户已滚动的值
|
|
153
|
+
this.reInitColumns()
|
|
143
154
|
}
|
|
144
155
|
},
|
|
145
156
|
computed: {
|
|
146
|
-
//
|
|
157
|
+
// 如果以下这些变量发生了变化,意味着需要重新计算各列范围,但应保留当前已选值
|
|
147
158
|
propsChange() {
|
|
148
|
-
return [this.
|
|
159
|
+
return [this.maxDate, this.minDate, this.minHour, this.maxHour, this.minMinute, this.maxMinute, this.minSecond, this.maxSecond, this.filter]
|
|
149
160
|
},
|
|
150
161
|
resolvedMaskStyle() {
|
|
151
162
|
if (this.upHasProp('maskStyle')) {
|
|
@@ -249,6 +260,38 @@
|
|
|
249
260
|
|
|
250
261
|
// 初始化hasInput展示
|
|
251
262
|
this.getInputValue(this.innerValue)
|
|
263
|
+
// 以外部值为基准,重置去重基准
|
|
264
|
+
this.lastEmitValue = this.innerValue
|
|
265
|
+
},
|
|
266
|
+
// 边界(minMinute等)变化时调用:以当前已选值(innerValue)为基准,
|
|
267
|
+
// 按新的边界重新校正并重建各列,而不是回退到外部modelValue。
|
|
268
|
+
// 这样在未点击确认前也不会丢失/重置用户已经滚动选择的值。
|
|
269
|
+
reInitColumns() {
|
|
270
|
+
let base = this.innerValue
|
|
271
|
+
if (base === undefined || base === null || base === '') {
|
|
272
|
+
// #ifdef VUE3
|
|
273
|
+
base = this.modelValue
|
|
274
|
+
// #endif
|
|
275
|
+
// #ifdef VUE2
|
|
276
|
+
base = this.value
|
|
277
|
+
// #endif
|
|
278
|
+
}
|
|
279
|
+
// correctValue会把小于新minMinute(或大于maxMinute)的值夹取到合法范围内,
|
|
280
|
+
// 例如原选中15:30、minMinute变为51时会被夹取为15:51
|
|
281
|
+
const corrected = this.correctValue(base)
|
|
282
|
+
const changed = corrected !== this.innerValue
|
|
283
|
+
this.innerValue = corrected
|
|
284
|
+
this.updateColumnValue(corrected)
|
|
285
|
+
this.getInputValue(corrected)
|
|
286
|
+
// 仅当已选值确实因边界收紧而被夹取变化时,才补发一次change通知外部;
|
|
287
|
+
// 值未变化则不上抛,避免边界变化引起的重复change
|
|
288
|
+
if (changed && this.lastEmitValue !== corrected) {
|
|
289
|
+
this.lastEmitValue = corrected
|
|
290
|
+
this.$emit('change', {
|
|
291
|
+
value: corrected,
|
|
292
|
+
mode: this.mode
|
|
293
|
+
})
|
|
294
|
+
}
|
|
252
295
|
},
|
|
253
296
|
// 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用
|
|
254
297
|
setFormatter(e) {
|
|
@@ -378,8 +421,14 @@
|
|
|
378
421
|
}
|
|
379
422
|
// 取出准确的合法值,防止超越边界的情况
|
|
380
423
|
selectValue = this.correctValue(selectValue)
|
|
424
|
+
// 边界变化会程序化重建各列并回填索引,picker-view在部分原生端会再次派发change,
|
|
425
|
+
// 若此时算出的值与上一次已上抛的值一致,说明并非用户真实操作,直接忽略,避免重复触发change
|
|
426
|
+
if (selectValue === this.lastEmitValue && selectValue === this.innerValue) {
|
|
427
|
+
return
|
|
428
|
+
}
|
|
381
429
|
this.innerValue = selectValue
|
|
382
430
|
this.updateColumnValue(selectValue)
|
|
431
|
+
this.lastEmitValue = selectValue
|
|
383
432
|
// 发出change时间,value为当前选中的时间戳
|
|
384
433
|
this.$emit('change', {
|
|
385
434
|
value: selectValue,
|
|
@@ -4,21 +4,21 @@
|
|
|
4
4
|
:style="movableAreaStyle"
|
|
5
5
|
>
|
|
6
6
|
<movable-area class="u-dragsort-area">
|
|
7
|
-
<movable-view v-for="(item, index) in list" :key="item.id" :id="`u-dragsort-item-${
|
|
8
|
-
class="u-dragsort-item" :class="{ 'dragging':
|
|
7
|
+
<movable-view v-for="(item, index) in list" :key="item.id" :id="`u-dragsort-item-${instanceId}-${item.id}`"
|
|
8
|
+
class="u-dragsort-item" :class="{ 'dragging': dragItemId === item.id, disabled: !draggable || item.draggable === false }"
|
|
9
9
|
:direction="direction === 'all' ? 'all' : direction" :x="item.x" :y="item.y" :inertia="false"
|
|
10
|
-
:disabled="!draggable ||
|
|
11
|
-
@touchstart="onTouchStart(
|
|
10
|
+
:disabled="!draggable || dragItemId === null || item.draggable === false" @change="onChange(item.id, $event)"
|
|
11
|
+
@touchstart="onTouchStart(item.id, $event)" @touchend="onTouchEnd" @touchcancel="onTouchEnd" @touchmove="onTouchMove">
|
|
12
12
|
<view class="u-dragsort-item-content">
|
|
13
13
|
<view
|
|
14
14
|
class="ui-dragSort-item-handler"
|
|
15
|
-
v-if="
|
|
15
|
+
v-if="hasHandler"
|
|
16
16
|
data-action="handler"
|
|
17
|
-
@touchstart="onTouchStart(
|
|
17
|
+
@touchstart="onTouchStart(item.id, $event)"
|
|
18
18
|
>
|
|
19
|
-
<slot name="handler" :item="item" :index="
|
|
19
|
+
<slot name="handler" :item="item" :index="getItemIndex(item.id)"></slot>
|
|
20
20
|
</view>
|
|
21
|
-
<slot :item="item" :index="
|
|
21
|
+
<slot :item="item" :index="getItemIndex(item.id)">
|
|
22
22
|
{{ item.label }}
|
|
23
23
|
</slot>
|
|
24
24
|
</view>
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
<script>
|
|
31
31
|
import { mpMixin } from '../../libs/mixin/mpMixin';
|
|
32
32
|
import { mixin } from '../../libs/mixin/mixin';
|
|
33
|
-
import { sleep } from '../../libs/function/index';
|
|
33
|
+
import { guid, sleep } from '../../libs/function/index';
|
|
34
34
|
export default {
|
|
35
35
|
name: 'u-dragsort',
|
|
36
36
|
// #ifdef MP
|
|
@@ -67,7 +67,9 @@ export default {
|
|
|
67
67
|
data() {
|
|
68
68
|
return {
|
|
69
69
|
list: [],
|
|
70
|
-
|
|
70
|
+
orderIds: [],
|
|
71
|
+
instanceId: guid(8),
|
|
72
|
+
dragItemId: null,
|
|
71
73
|
sortChanged: false,
|
|
72
74
|
itemHeight: 0,
|
|
73
75
|
itemWidth: 0,
|
|
@@ -99,6 +101,9 @@ export default {
|
|
|
99
101
|
width: '100%'
|
|
100
102
|
}
|
|
101
103
|
}
|
|
104
|
+
},
|
|
105
|
+
hasHandler() {
|
|
106
|
+
return !!(this.$slots.handler || this.$slots.$handler)
|
|
102
107
|
}
|
|
103
108
|
},
|
|
104
109
|
emits: ['drag-end'],
|
|
@@ -110,8 +115,17 @@ export default {
|
|
|
110
115
|
},
|
|
111
116
|
methods: {
|
|
112
117
|
initList() {
|
|
118
|
+
this.orderIds = this.initialList.map(item => item.id)
|
|
119
|
+
const currentItems = new Map(this.list.map(item => [item.id, item]))
|
|
120
|
+
const initialItems = new Map(this.initialList.map(item => [item.id, item]))
|
|
121
|
+
const renderIds = [
|
|
122
|
+
...this.list.map(item => item.id).filter(itemId => initialItems.has(itemId)),
|
|
123
|
+
...this.orderIds.filter(itemId => !currentItems.has(itemId))
|
|
124
|
+
]
|
|
113
125
|
// 初始化列表项的位置
|
|
114
|
-
this.list =
|
|
126
|
+
this.list = renderIds.map(itemId => {
|
|
127
|
+
const item = initialItems.get(itemId)
|
|
128
|
+
const index = this.getItemIndex(itemId)
|
|
115
129
|
let x
|
|
116
130
|
let y
|
|
117
131
|
|
|
@@ -136,6 +150,9 @@ export default {
|
|
|
136
150
|
}
|
|
137
151
|
})
|
|
138
152
|
},
|
|
153
|
+
getItemIndex(itemId) {
|
|
154
|
+
return this.orderIds.indexOf(itemId)
|
|
155
|
+
},
|
|
139
156
|
async calculateItemSize() {
|
|
140
157
|
// 计算项目尺寸
|
|
141
158
|
await sleep(30);
|
|
@@ -175,12 +192,14 @@ export default {
|
|
|
175
192
|
},
|
|
176
193
|
updatePositions(isDragging) {
|
|
177
194
|
// 更新所有项目的位置
|
|
178
|
-
this.list = this.list.map(
|
|
195
|
+
this.list = this.list.map(item => {
|
|
179
196
|
// 当前正在拖动的项目保持拖动位置不动,避免抖动
|
|
180
|
-
if (isDragging && this.
|
|
197
|
+
if (isDragging && this.dragItemId === item.id) {
|
|
181
198
|
return item
|
|
182
199
|
}
|
|
183
200
|
|
|
201
|
+
const index = this.getItemIndex(item.id)
|
|
202
|
+
|
|
184
203
|
if (this.direction === 'vertical') {
|
|
185
204
|
return {
|
|
186
205
|
...item,
|
|
@@ -208,26 +227,31 @@ export default {
|
|
|
208
227
|
}
|
|
209
228
|
})
|
|
210
229
|
},
|
|
211
|
-
onTouchStart(
|
|
212
|
-
if (this
|
|
230
|
+
onTouchStart(itemId, e) {
|
|
231
|
+
if (this.hasHandler && e.currentTarget.dataset.action !== 'handler') {
|
|
213
232
|
return
|
|
214
233
|
}
|
|
234
|
+
const index = this.list.findIndex(item => item.id === itemId)
|
|
235
|
+
if (index === -1) return
|
|
215
236
|
// 全局禁用或单项禁用时,不进入拖拽态,避免 Android 上小范围漂浮/阴影
|
|
216
237
|
if (!this.draggable || this.list[index]?.draggable === false) return;
|
|
217
238
|
if (this.timer) clearTimeout(this.timer);
|
|
218
239
|
this.sortChanged = false;
|
|
219
|
-
this.
|
|
240
|
+
this.dragItemId = itemId;
|
|
220
241
|
},
|
|
221
242
|
onTouchMove(e) {
|
|
222
|
-
if (this.
|
|
243
|
+
if (this.dragItemId !== null) {
|
|
223
244
|
// 目前只对H5生效, 如果该组件放置在开启了下拉刷新的scroll-view中, 向下拖动item还是会触发下拉刷新
|
|
224
245
|
e.stopPropagation()
|
|
225
246
|
e.preventDefault()
|
|
226
247
|
}
|
|
227
248
|
},
|
|
228
|
-
onChange(
|
|
249
|
+
onChange(itemId, event) {
|
|
229
250
|
if (!event.detail.source || event.detail.source !== 'touch') return;
|
|
230
251
|
|
|
252
|
+
const index = this.getItemIndex(itemId)
|
|
253
|
+
if (index === -1) return;
|
|
254
|
+
|
|
231
255
|
this.currentPosition.x = event.detail.x;
|
|
232
256
|
this.currentPosition.y = event.detail.y;
|
|
233
257
|
|
|
@@ -274,11 +298,9 @@ export default {
|
|
|
274
298
|
}
|
|
275
299
|
},
|
|
276
300
|
reorderItems(fromIndex, toIndex) {
|
|
277
|
-
const
|
|
278
|
-
this.
|
|
301
|
+
const movedItemId = this.orderIds.splice(fromIndex, 1)[0];
|
|
302
|
+
this.orderIds.splice(toIndex, 0, movedItemId);
|
|
279
303
|
|
|
280
|
-
// 更新当前拖拽项目的新索引
|
|
281
|
-
this.dragIndex = toIndex;
|
|
282
304
|
this.sortChanged = true;
|
|
283
305
|
|
|
284
306
|
// 更新所有项目的位置
|
|
@@ -291,25 +313,29 @@ export default {
|
|
|
291
313
|
},
|
|
292
314
|
onTouchEnd() {
|
|
293
315
|
// 未发生位移
|
|
294
|
-
if (this.
|
|
316
|
+
if (this.dragItemId === null) return
|
|
317
|
+
|
|
318
|
+
const dragItem = this.list.find(item => item.id === this.dragItemId)
|
|
319
|
+
if (!dragItem) return
|
|
295
320
|
|
|
296
321
|
// 0.001是为了解决拖动过快等某些极限场景下位置还原不生效问题
|
|
297
322
|
if (this.direction === 'horizontal') {
|
|
298
|
-
|
|
323
|
+
dragItem.x = this.currentPosition.x + 0.001;
|
|
299
324
|
} else if (this.direction === 'vertical' || this.direction === 'all') {
|
|
300
|
-
|
|
301
|
-
|
|
325
|
+
dragItem.y = this.currentPosition.y + 0.001;
|
|
326
|
+
dragItem.x = this.currentPosition.x + 0.001;
|
|
302
327
|
}
|
|
303
328
|
|
|
304
329
|
// 重置到位置,需要延迟触发动,否则无效。
|
|
305
330
|
sleep(50).then(() => {
|
|
306
331
|
this.updatePositions();
|
|
307
332
|
if (this.sortChanged) {
|
|
308
|
-
this
|
|
333
|
+
const sortedList = this.orderIds.map(itemId => this.list.find(item => item.id === itemId));
|
|
334
|
+
this.$emit('drag-end', sortedList);
|
|
309
335
|
this.sortChanged = false;
|
|
310
336
|
}
|
|
311
337
|
this.timer = setTimeout(() => {
|
|
312
|
-
this.
|
|
338
|
+
this.dragItemId = null
|
|
313
339
|
}, 600)
|
|
314
340
|
});
|
|
315
341
|
}
|
|
@@ -319,6 +345,7 @@ export default {
|
|
|
319
345
|
handler() {
|
|
320
346
|
this.$nextTick(() => {
|
|
321
347
|
this.initList();
|
|
348
|
+
this.updatePositions();
|
|
322
349
|
});
|
|
323
350
|
},
|
|
324
351
|
// deep: true
|
|
@@ -372,9 +399,6 @@ export default {
|
|
|
372
399
|
position: relative;
|
|
373
400
|
padding: 0;
|
|
374
401
|
box-sizing: border-box;
|
|
375
|
-
border: 1px solid var(--up-border-color, rgba(125, 126, 128, 0.35));
|
|
376
|
-
border-radius: 8px;
|
|
377
|
-
background-color: var(--up-card-bg-color, #ffffff);
|
|
378
402
|
}
|
|
379
403
|
}
|
|
380
404
|
|
|
@@ -72,10 +72,19 @@
|
|
|
72
72
|
export default {
|
|
73
73
|
name: 'u-icon',
|
|
74
74
|
beforeCreate() {
|
|
75
|
-
|
|
75
|
+
// #ifndef APP-VUE
|
|
76
|
+
if (!fontUtil.isLoaded()) {
|
|
76
77
|
fontUtil.loadFont();
|
|
77
78
|
}
|
|
79
|
+
// #endif
|
|
78
80
|
},
|
|
81
|
+
mounted() {
|
|
82
|
+
// #ifdef APP-VUE
|
|
83
|
+
if (!fontUtil.isLoaded()) {
|
|
84
|
+
fontUtil.loadFont();
|
|
85
|
+
}
|
|
86
|
+
// #endif
|
|
87
|
+
},
|
|
79
88
|
data() {
|
|
80
89
|
return {
|
|
81
90
|
}
|
|
@@ -161,8 +170,8 @@
|
|
|
161
170
|
$u-icon-error: $u-error !default;
|
|
162
171
|
$u-icon-label-line-height:1 !default;
|
|
163
172
|
|
|
164
|
-
/* #ifdef MP-QQ || MP-TOUTIAO || MP-BAIDU || MP-KUAISHOU || MP-XHS */
|
|
165
|
-
// App
|
|
173
|
+
/* #ifdef APP || MP-QQ || MP-TOUTIAO || MP-BAIDU || MP-KUAISHOU || MP-XHS */
|
|
174
|
+
// 默认保留App端CSS字体,兼容未启用UniUpRoot的老项目;启用UniUpRoot后构建插件会移除App条件并改用本地字体。
|
|
166
175
|
@font-face {
|
|
167
176
|
font-family: 'uicon-iconfont';
|
|
168
177
|
src: url('https://at.alicdn.com/t/font_2225171_8kdcwk4po24.ttf') format('truetype');
|
|
@@ -1,28 +1,74 @@
|
|
|
1
1
|
import config from '../../libs/config/config';
|
|
2
|
-
// #ifdef APP || APP-NVUE
|
|
3
|
-
import iconFontUrl from './upicon.ttf?url';
|
|
4
|
-
// #endif
|
|
5
2
|
|
|
6
3
|
const iconFontFamily = 'uicon-iconfont';
|
|
4
|
+
const appIconFontUrl = '_www/static/app-plus/uview-plus/upicon.ttf';
|
|
5
|
+
const useAppStaticIconFont = false;
|
|
7
6
|
|
|
8
7
|
let params = {
|
|
9
|
-
loaded: false
|
|
8
|
+
loaded: false,
|
|
9
|
+
loading: false
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// #ifdef APP-VUE
|
|
13
|
+
const appVueLoadedPages = new WeakSet();
|
|
14
|
+
const appVueLoadingPages = new WeakSet();
|
|
15
|
+
// #endif
|
|
16
|
+
|
|
17
|
+
const getCurrentAppVuePage = () => {
|
|
18
|
+
// #ifdef APP-VUE
|
|
19
|
+
try {
|
|
20
|
+
if (typeof getCurrentPages !== 'function') return null;
|
|
21
|
+
const pages = getCurrentPages();
|
|
22
|
+
if (!Array.isArray(pages) || pages.length === 0) return null;
|
|
23
|
+
const page = pages[pages.length - 1];
|
|
24
|
+
return page && (typeof page === 'object' || typeof page === 'function') ? page : null;
|
|
25
|
+
} catch (e) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
// #endif
|
|
29
|
+
return null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const isLoaded = () => {
|
|
33
|
+
// #ifdef APP-VUE
|
|
34
|
+
const page = getCurrentAppVuePage();
|
|
35
|
+
return !!page && appVueLoadedPages.has(page);
|
|
36
|
+
// #endif
|
|
37
|
+
return params.loaded;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const getAppIconUrl = () => {
|
|
41
|
+
// #ifdef APP || APP-NVUE
|
|
42
|
+
if (!useAppStaticIconFont) {
|
|
43
|
+
return config.iconUrl;
|
|
44
|
+
}
|
|
45
|
+
if (typeof plus !== 'undefined' && plus.io && typeof plus.io.convertLocalFileSystemURL === 'function') {
|
|
46
|
+
const iconFontPath = plus.io.convertLocalFileSystemURL(appIconFontUrl);
|
|
47
|
+
if (iconFontPath) return iconFontPath;
|
|
48
|
+
}
|
|
49
|
+
return appIconFontUrl;
|
|
50
|
+
// #endif
|
|
51
|
+
return '';
|
|
10
52
|
};
|
|
11
53
|
|
|
12
54
|
const getIconUrl = () => {
|
|
13
55
|
// #ifdef APP || APP-NVUE
|
|
14
|
-
return
|
|
56
|
+
return getAppIconUrl();
|
|
15
57
|
// #endif
|
|
16
58
|
return config.iconUrl;
|
|
17
59
|
};
|
|
18
60
|
|
|
19
61
|
const markFontLoaded = () => {
|
|
20
|
-
// App
|
|
21
|
-
// #ifdef APP
|
|
62
|
+
// App Vue的字体注册只对当前页面WebView生效,由页面级状态在成功回调中记录。
|
|
63
|
+
// #ifdef APP-VUE
|
|
64
|
+
return;
|
|
65
|
+
// #endif
|
|
66
|
+
params.loading = false;
|
|
67
|
+
// #ifdef APP-NVUE
|
|
22
68
|
params.loaded = true;
|
|
23
69
|
return;
|
|
24
70
|
// #endif
|
|
25
|
-
//
|
|
71
|
+
// 非App Vue平台由loadFontOnce决定成功后是否复用本次加载结果。
|
|
26
72
|
if (config.loadFontOnce) {
|
|
27
73
|
params.loaded = true;
|
|
28
74
|
}
|
|
@@ -30,8 +76,20 @@ const markFontLoaded = () => {
|
|
|
30
76
|
|
|
31
77
|
// 加载字体方法
|
|
32
78
|
const loadFont = () => {
|
|
79
|
+
// #ifdef APP-VUE
|
|
80
|
+
const appVuePage = getCurrentAppVuePage();
|
|
81
|
+
if (!appVuePage || appVueLoadedPages.has(appVuePage) || appVueLoadingPages.has(appVuePage)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
appVueLoadingPages.add(appVuePage);
|
|
85
|
+
// #endif
|
|
86
|
+
// #ifndef APP-VUE
|
|
87
|
+
if (params.loaded || params.loading) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
params.loading = true;
|
|
91
|
+
// #endif
|
|
33
92
|
const iconUrl = getIconUrl();
|
|
34
|
-
markFontLoaded();
|
|
35
93
|
// #ifdef APP-NVUE
|
|
36
94
|
// nvue通过weex的dom模块引入字体,相关文档地址如下:
|
|
37
95
|
// https://weex.apache.org/zh/docs/modules/dom.html#addrule
|
|
@@ -46,16 +104,28 @@ const loadFont = () => {
|
|
|
46
104
|
'src': `url('${config.customIcon.url}')`
|
|
47
105
|
});
|
|
48
106
|
}
|
|
107
|
+
markFontLoaded();
|
|
49
108
|
// #endif
|
|
50
|
-
// #ifdef APP || H5 || MP-WEIXIN || MP-ALIPAY
|
|
109
|
+
// #ifdef APP-VUE || H5 || MP-WEIXIN || MP-ALIPAY
|
|
51
110
|
uni.loadFontFace({
|
|
52
111
|
global: true, // 是否全局生效。微信小程序 '2.10.0'起支持全局生效,需在 app.vue 中调用。
|
|
53
112
|
family: iconFontFamily,
|
|
54
113
|
source: 'url("' + iconUrl + '")',
|
|
55
114
|
success() {
|
|
115
|
+
// #ifdef APP-VUE
|
|
116
|
+
appVueLoadingPages.delete(appVuePage);
|
|
117
|
+
appVueLoadedPages.add(appVuePage);
|
|
118
|
+
// #endif
|
|
119
|
+
markFontLoaded();
|
|
56
120
|
// console.log('内置字体图标加载成功');
|
|
57
121
|
},
|
|
58
122
|
fail() {
|
|
123
|
+
// #ifdef APP-VUE
|
|
124
|
+
appVueLoadingPages.delete(appVuePage);
|
|
125
|
+
// #endif
|
|
126
|
+
// #ifndef APP-VUE
|
|
127
|
+
params.loading = false;
|
|
128
|
+
// #endif
|
|
59
129
|
// console.error('内置字体图标加载出错');
|
|
60
130
|
}
|
|
61
131
|
});
|
|
@@ -86,5 +156,6 @@ const loadFont = () => {
|
|
|
86
156
|
|
|
87
157
|
export default {
|
|
88
158
|
params: params,
|
|
159
|
+
isLoaded,
|
|
89
160
|
loadFont
|
|
90
161
|
}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { mixin } from '../../libs/mixin/mixin';
|
|
21
21
|
import { sleep, error } from '../../libs/function/index';
|
|
22
22
|
// #ifdef APP-NVUE
|
|
23
|
-
// 由于weex
|
|
23
|
+
// 由于weex不支持百分比单位,这里需要通过dom查询组件的宽度
|
|
24
24
|
const dom = uni.requireNativePlugin('dom')
|
|
25
25
|
// #endif
|
|
26
26
|
/**
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
import { addUnit, getWindowInfo, sleep, getPx } from '../../libs/function/index';
|
|
109
109
|
|
|
110
110
|
// #ifdef APP-NVUE
|
|
111
|
-
// 由于weex
|
|
111
|
+
// 由于weex不支持百分比单位,这里需要通过dom查询组件的宽度
|
|
112
112
|
const dom = uni.requireNativePlugin('dom')
|
|
113
113
|
// #endif
|
|
114
114
|
/**
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const LINE_BREAK_PATTERN = /\r\n|\r|\n/
|
|
2
|
+
|
|
3
|
+
function normalizeParagraphs(content) {
|
|
4
|
+
const values = Array.isArray(content) ? content : [content == null ? '' : content]
|
|
5
|
+
return values.reduce((paragraphs, value) => {
|
|
6
|
+
String(value == null ? '' : value)
|
|
7
|
+
.split(LINE_BREAK_PATTERN)
|
|
8
|
+
.forEach((text) => paragraphs.push(text))
|
|
9
|
+
return paragraphs
|
|
10
|
+
}, [])
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function normalizeContent(content) {
|
|
14
|
+
const sourceParagraphs = normalizeParagraphs(content)
|
|
15
|
+
const hasContent = sourceParagraphs.some((text) => text.length > 0)
|
|
16
|
+
if (!hasContent) {
|
|
17
|
+
return {
|
|
18
|
+
paragraphs: [],
|
|
19
|
+
text: '',
|
|
20
|
+
length: 0
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let offset = 0
|
|
25
|
+
const paragraphs = sourceParagraphs.map((text, index) => {
|
|
26
|
+
const startOffset = offset
|
|
27
|
+
const endOffset = startOffset + text.length
|
|
28
|
+
offset = endOffset + 1
|
|
29
|
+
return {
|
|
30
|
+
index,
|
|
31
|
+
text,
|
|
32
|
+
startOffset,
|
|
33
|
+
endOffset
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const text = paragraphs.map((paragraph) => paragraph.text).join('\n')
|
|
38
|
+
return {
|
|
39
|
+
paragraphs,
|
|
40
|
+
text,
|
|
41
|
+
length: text.length
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function normalizeProgress(progress, chapter) {
|
|
46
|
+
const normalizedContent = normalizeContent(chapter && chapter.content)
|
|
47
|
+
const contentLength = normalizedContent.length
|
|
48
|
+
const requestedOffset = Number(progress && progress.charOffset)
|
|
49
|
+
const charOffset = Math.min(
|
|
50
|
+
contentLength,
|
|
51
|
+
Math.max(0, Number.isFinite(requestedOffset) ? requestedOffset : 0)
|
|
52
|
+
)
|
|
53
|
+
const requestedPageIndex = Number(progress && progress.pageIndex)
|
|
54
|
+
const chapterIndex = Number(chapter && chapter.index)
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
chapterId: progress && progress.chapterId != null
|
|
58
|
+
? progress.chapterId
|
|
59
|
+
: chapter && chapter.id != null
|
|
60
|
+
? chapter.id
|
|
61
|
+
: '',
|
|
62
|
+
chapterIndex: Number.isFinite(chapterIndex)
|
|
63
|
+
? chapterIndex
|
|
64
|
+
: Number(progress && progress.chapterIndex) || 0,
|
|
65
|
+
pageIndex: Math.max(0, Number.isFinite(requestedPageIndex) ? requestedPageIndex : 0),
|
|
66
|
+
pageCount: Math.max(0, Number(progress && progress.pageCount) || 0),
|
|
67
|
+
charOffset,
|
|
68
|
+
chapterProgress: contentLength
|
|
69
|
+
? Math.min(1, Math.max(0, charOffset / contentLength))
|
|
70
|
+
: 0,
|
|
71
|
+
totalProgress: Math.min(1, Math.max(0, Number(progress && progress.totalProgress) || 0)),
|
|
72
|
+
scrollTop: Math.max(0, Number(progress && progress.scrollTop) || 0),
|
|
73
|
+
updatedAt: Number(progress && progress.updatedAt) || 0
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default {
|
|
78
|
+
normalizeContent,
|
|
79
|
+
normalizeProgress
|
|
80
|
+
}
|