cbvirtua 1.0.84 → 1.0.86
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/FireShot/a (1).png +0 -0
- package/FireShot/a (2).png +0 -0
- package/leetcode-javascript-master.zip +0 -0
- package/package.json +1 -1
- package//346/226/260/345/273/272 /346/226/207/346/234/254/346/226/207/346/241/243 (3).zip +0 -0
- package/2.png +0 -0
- package/8.zip +0 -0
- package/FireShot Capture 007 - /343/200/220async_await /345/216/237/347/220/206/343/200/221/344/271/213 Thunk /345/207/275/346/225/260/345/222/214 co /345/207/275/346/225/260/345/216/237/347/220/206/345/276/210/345/244/232/346/234/213/345/217/213/347/233/262/347/233/256/347/232/204/350/256/260/344/275/217/344/272/206 Promise + Gene - /346/216/230/351/207/221_ - [juejin.cn].7z +0 -0
- package/FireShot Capture 020 - CSS /347/216/260/345/234/250/347/273/210/344/272/216/346/224/257/346/214/201/351/253/230/345/272/246 auto /350/277/207/346/270/241/345/212/250/347/224/273/344/272/206/344/274/227/346/211/200/345/221/250/347/237/245/357/274/214/346/234/211/344/272/233/345/261/236/346/200/247/346/230/257/344/270/215/346/224/257/346/214/201/350/277/207/346/270/241/345/212/250/347/224/273/347/232/204/357/274/214/346/257/224/345/246/202/351/253/230/345/272/246auto /346/225/210/346/236/234/345/246/202/344/270/213 /350/246/201/345/256/236/347/216/260/350/277/207 - /346/216/230/351/207/221_ - [juejin.cn].7z +0 -0
- package//346/226/260/345/273/272 /346/226/207/346/234/254/346/226/207/346/241/243 (2).txt" +0 -272
- package//346/226/260/345/273/272 /346/226/207/346/234/254/346/226/207/346/241/243 (3).txt" +0 -77
- package//346/226/260/345/273/272 /346/226/207/346/234/254/346/226/207/346/241/243 (4).txt" +0 -45
- package//346/226/260/345/273/272 /346/226/207/346/234/254/346/226/207/346/241/243 (5).txt" +0 -49
- package//346/226/260/345/273/272 /346/226/207/346/234/254/346/226/207/346/241/243.txt" +0 -349
- package//346/226/260/345/273/272 /346/226/207/346/234/254/346/226/207/346/241/24311.txt" +0 -113
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
Binary file
|
package/2.png
DELETED
|
Binary file
|
package/8.zip
DELETED
|
Binary file
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
vue自定义指令v-input:限制输入框只能输入数字、字母、中文等规则【附源码】
|
|
2
|
-
指令封装
|
|
3
|
-
directives/input/index.js
|
|
4
|
-
|
|
5
|
-
/*
|
|
6
|
-
限制输入框只能输入数字、字母、中文等规则
|
|
7
|
-
|
|
8
|
-
使用指令:v-input
|
|
9
|
-
|
|
10
|
-
修饰符参数说明:
|
|
11
|
-
v-input.num 只能输入数字,默认不传修饰符,会自动限制只能输入数字
|
|
12
|
-
v-input.intp 只能输入正整数
|
|
13
|
-
v-input.num_alp 只能输入数字和字母
|
|
14
|
-
v-input.num_alp_blank 只能输入数字、字母、空格
|
|
15
|
-
v-input.num_alp_sym 只能输入数字和字母、英文符号、空格
|
|
16
|
-
v-input.float 只能输入数字和小数点 v-input.float="2" 表示小数位数为2,默认小数位数为2,v-input.float="2"可以简写为v-input.float
|
|
17
|
-
v-input.no_emoji 不能输入表情符号
|
|
18
|
-
|
|
19
|
-
*/
|
|
20
|
-
// 只能输入数字
|
|
21
|
-
function num(el) {
|
|
22
|
-
el.value = el.value.replace(/\D+/g, '')
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// 只能输入正整数
|
|
26
|
-
function intp(el) {
|
|
27
|
-
const value = el.value.replace(/\D+/g, '') // 去掉非数字字符
|
|
28
|
-
el.value = /^[1-9][0-9]*$/.test(value) ? value : value.replace(/^0+/, '') // 确保为正整数,去掉前导零
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// 只能输入数字和字母
|
|
32
|
-
function num_alp(el) {
|
|
33
|
-
el.value = el.value.replace(/[^A-Za-z0-9]/g, '')
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// 只能输入数字、字母、空格
|
|
37
|
-
function num_alp_blank(el) {
|
|
38
|
-
const regex = /[^a-zA-Z0-9 ]/g
|
|
39
|
-
el.value = el.value.replace(regex, '')
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// 只能输入数字、字母、英文符号、空格
|
|
43
|
-
function num_alp_sym(el) {
|
|
44
|
-
const regex = /[^a-zA-Z0-9`~!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/? ]/g
|
|
45
|
-
el.value = el.value.replace(regex, '')
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// 只能输入数字和小数点,n表示小数位数
|
|
49
|
-
function float(el, n) {
|
|
50
|
-
let value = el.value
|
|
51
|
-
value = value.replace(/[^\d.]/g, '') // 能数字和小数点
|
|
52
|
-
value = value.replace(/^\./g, '') // 去掉开头的点
|
|
53
|
-
value = value.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.') // 处理多个点的情况
|
|
54
|
-
if (n && Number(n) > 0) {
|
|
55
|
-
const d = new Array(Number(n)).fill('\\d').join('') // 构建正则表达式
|
|
56
|
-
const reg = new RegExp(`^(\\-)*(\\d+)\\.(${d}).*$`, 'ig')
|
|
57
|
-
value = value.replace(reg, '$1$2.$3') // 限制小数位数
|
|
58
|
-
}
|
|
59
|
-
// if (value && !value.includes('.')) {
|
|
60
|
-
// value = Number(value).toString() // 去掉开头多个0
|
|
61
|
-
// }
|
|
62
|
-
el.value = value
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// 限制表情:😀😂❤️🌟🎉🌍🐶☺
|
|
66
|
-
function no_emoji(el) {
|
|
67
|
-
const regex =
|
|
68
|
-
/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E6}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{1F900}-\u{1F9FF}\u{263A}]+/gu
|
|
69
|
-
el.value = el.value.replace(regex, '')
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// 这里扩展限制的类型
|
|
73
|
-
const map = { num, intp, num_alp, num_alp_blank, num_alp_sym, float, no_emoji }
|
|
74
|
-
|
|
75
|
-
export default {
|
|
76
|
-
bind(el, binding, vnode) {
|
|
77
|
-
el = el.querySelector('.el-input__inner') || el.querySelector('.el-textarea__inner') || el
|
|
78
|
-
let lock = false // 标记是否需要锁定输入框
|
|
79
|
-
let isHandling = false // 标记是否正在处理
|
|
80
|
-
let lastValue = null
|
|
81
|
-
// input事件处理函数
|
|
82
|
-
const handler = () => {
|
|
83
|
-
if (lock) return // 如果当前为锁定状态,则不进行处理
|
|
84
|
-
if (isHandling) return // 如果已经在处理中,则不进行处理
|
|
85
|
-
if (el.value === lastValue) return // 输入内容没有变化,则不进行处理
|
|
86
|
-
isHandling = true // 设置标记为处理中
|
|
87
|
-
const modifiers = Object.keys(binding.modifiers)
|
|
88
|
-
const newModifier = modifiers[0] || 'num'
|
|
89
|
-
map[newModifier](el, binding.value || 2)
|
|
90
|
-
lastValue = el.value // 记录当前输入框的值
|
|
91
|
-
Promise.resolve().then(() => {
|
|
92
|
-
// 异步处理,场景:火狐浏览器中,需要在最后派发input事件
|
|
93
|
-
el.dispatchEvent(new Event('input'))
|
|
94
|
-
})
|
|
95
|
-
isHandling = false // 处理完毕后设置标记为非处理状态
|
|
96
|
-
}
|
|
97
|
-
el.addEventListener('input', handler)
|
|
98
|
-
// compositionstart和compositionend事件解决的bug场景:限制只能输入数字的输入框,先输入数字,再切换为中文输入法,输入字母时,会将原来的数字删掉
|
|
99
|
-
el.addEventListener('compositionstart', () => {
|
|
100
|
-
lock = true
|
|
101
|
-
})
|
|
102
|
-
el.addEventListener('compositionend', () => {
|
|
103
|
-
lock = false
|
|
104
|
-
el.dispatchEvent(new Event('input'))
|
|
105
|
-
})
|
|
106
|
-
// 当指令与元素解绑时,移除事件监听器
|
|
107
|
-
vnode.context.$once('hook:destroyed', () => {
|
|
108
|
-
el.removeEventListener('input', handler)
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
directives/index.js
|
|
114
|
-
|
|
115
|
-
import input from './input'
|
|
116
|
-
|
|
117
|
-
const directives = { input }
|
|
118
|
-
|
|
119
|
-
Object.keys(directives).forEach(key => {
|
|
120
|
-
Vue.directive(key, directives[key])
|
|
121
|
-
})
|
|
122
|
-
main.js
|
|
123
|
-
|
|
124
|
-
import './directives'
|
|
125
|
-
注意事项:
|
|
126
|
-
指令的封装不要用keyup事件,当限制只能输入数字时,中文输入法下,输入字母,鼠标点击输入法提示的中文,会将中文输入至文本框中,原因是鼠标点击事件不会触发keyup,用input事件会规避掉这个问题
|
|
127
|
-
使用input事件时,控制台会提示堆栈溢出,需要借助isHandling,执行handler时,先判断是否需要处理
|
|
128
|
-
当限制只能输入数字时,先输入数字,在切换为中文输入法,输入字母时,会将原来的数字删掉,这个问题可以借助compositionstart和compositionend事件解决
|
|
129
|
-
使用
|
|
130
|
-
image.png
|
|
131
|
-
|
|
132
|
-
组件封装
|
|
133
|
-
CustomInput/index.vue
|
|
134
|
-
|
|
135
|
-
<!--
|
|
136
|
-
使用时,直接用 <CustomInput /> 替代 <el-input />
|
|
137
|
-
|
|
138
|
-
limit:限制输入类型,可选值:num、intp、numAlp、numAlpBlank、numAlpSym、float、noEmoji,默认为num
|
|
139
|
-
decimals:当limit为float类型时,限制小数位数
|
|
140
|
-
|
|
141
|
-
支持插槽:
|
|
142
|
-
<CustomInput v-model="subItem.regionHigh" limit="float" maxlength="12" placeholder="请输入" clearable>
|
|
143
|
-
<template slot="prepend">H</template>
|
|
144
|
-
<template slot="append">cm</template>
|
|
145
|
-
</CustomInput>
|
|
146
|
-
|
|
147
|
-
支持拓展:
|
|
148
|
-
1. 定义限制类型的函数
|
|
149
|
-
2. 在map对象中添加此类型
|
|
150
|
-
3. 完善新类型的注释
|
|
151
|
-
-->
|
|
152
|
-
|
|
153
|
-
<template>
|
|
154
|
-
<el-input v-model="inputValue" v-bind="$attrs" v-on="$listeners" @input="handleInput">
|
|
155
|
-
<span v-for="{ name, text } of slotList" :slot="name" :key="text">{{ text }}</span>
|
|
156
|
-
</el-input>
|
|
157
|
-
</template>
|
|
158
|
-
|
|
159
|
-
<script>
|
|
160
|
-
const map = {
|
|
161
|
-
num,
|
|
162
|
-
intp,
|
|
163
|
-
numAlp,
|
|
164
|
-
numAlpBlank,
|
|
165
|
-
numAlpSym,
|
|
166
|
-
float,
|
|
167
|
-
noEmoji
|
|
168
|
-
}
|
|
169
|
-
export default {
|
|
170
|
-
name: 'CustomInput',
|
|
171
|
-
model: { prop: 'value', event: 'changeValue' },
|
|
172
|
-
props: {
|
|
173
|
-
value: { type: [String, Number], default: '' },
|
|
174
|
-
limit: { type: String, default: 'num' }, // 限制输入类型,可选值:num、intp、numAlp、numAlpBlank、numAlpSym、float、noEmoji,默认为num
|
|
175
|
-
decimals: { type: Number, default: 2 } // float类型限制小数位数
|
|
176
|
-
},
|
|
177
|
-
data() {
|
|
178
|
-
const { value } = this
|
|
179
|
-
return { inputValue: value || value === 0 ? String(value) : '', slotList: [] }
|
|
180
|
-
},
|
|
181
|
-
watch: {
|
|
182
|
-
// 手动设置value时,触发handleInput,将不符合限制的字符过滤掉
|
|
183
|
-
value: {
|
|
184
|
-
handler(val) {
|
|
185
|
-
this.handleInput(String(val))
|
|
186
|
-
},
|
|
187
|
-
immediate: true
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
created() {
|
|
191
|
-
this.initSlot()
|
|
192
|
-
},
|
|
193
|
-
methods: {
|
|
194
|
-
handleInput(val) {
|
|
195
|
-
if (!this.limit) return
|
|
196
|
-
const filteredInput = map[this.limit](val, this.decimals)
|
|
197
|
-
this.$emit('changeValue', filteredInput)
|
|
198
|
-
this.inputValue = filteredInput
|
|
199
|
-
},
|
|
200
|
-
initSlot() {
|
|
201
|
-
this.$nextTick(() => {
|
|
202
|
-
const { prepend = [], append = [] } = this.$slots
|
|
203
|
-
const prependList = prepend.map((item) => ({ name: 'prepend', text: item.text }))
|
|
204
|
-
const appendList = append.map((item) => ({ name: 'append', text: item.text }))
|
|
205
|
-
const slotList = prependList.concat(appendList)
|
|
206
|
-
this.slotList = slotList
|
|
207
|
-
})
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
// 只能输入数字
|
|
212
|
-
function num(val) {
|
|
213
|
-
const newVal = val.replace(/[^0-9]/g, '')
|
|
214
|
-
return newVal
|
|
215
|
-
}
|
|
216
|
-
// 只能输入正整数
|
|
217
|
-
function intp(val) {
|
|
218
|
-
let newVal = val.replace(/[^0-9]/g, '') // 去掉非数字字符
|
|
219
|
-
newVal = /^[1-9][0-9]*$/.test(newVal) ? newVal : newVal.replace(/^0+/, '') // 确保为正整数,去掉前导零
|
|
220
|
-
return newVal
|
|
221
|
-
}
|
|
222
|
-
// 只能输入数字和字母
|
|
223
|
-
function numAlp(val) {
|
|
224
|
-
const newVal = val.replace(/[^A-Za-z0-9]/g, '')
|
|
225
|
-
return newVal
|
|
226
|
-
}
|
|
227
|
-
// 只能输入数字、字母、空格
|
|
228
|
-
function numAlpBlank(val) {
|
|
229
|
-
const newVal = val.replace(/[^a-zA-Z0-9 ]/g, '')
|
|
230
|
-
return newVal
|
|
231
|
-
}
|
|
232
|
-
// 只能输入数字、字母、英文符号、空格
|
|
233
|
-
function numAlpSym(val) {
|
|
234
|
-
const regex = /[^a-zA-Z0-9`~!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/? ]/g
|
|
235
|
-
const newVal = val.replace(regex, '')
|
|
236
|
-
return newVal
|
|
237
|
-
}
|
|
238
|
-
// 只能输入数字和小数点,n表示小数位数
|
|
239
|
-
function float(val, n) {
|
|
240
|
-
let newVal = val.replace(/[^\d.]/g, '') // 能数字和小数点
|
|
241
|
-
newVal = newVal.replace(/^\./g, '') // 去掉开头的点
|
|
242
|
-
newVal = newVal.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.') // 处理多个点的情况
|
|
243
|
-
if (n && Number(n) > 0) {
|
|
244
|
-
const d = new Array(Number(n)).fill('\\d').join('') // 构建正则表达式
|
|
245
|
-
const reg = new RegExp(`^(\\-)*(\\d+)\\.(${d}).*$`, 'ig')
|
|
246
|
-
newVal = newVal.replace(reg, '$1$2.$3') // 限制小数位数
|
|
247
|
-
}
|
|
248
|
-
if (newVal && !newVal.includes('.')) {
|
|
249
|
-
// value = value.replace(/^0+/, '')
|
|
250
|
-
// value = Number(value).toString() // 去掉开头多个0
|
|
251
|
-
}
|
|
252
|
-
return newVal
|
|
253
|
-
}
|
|
254
|
-
// 限制不可输入表情
|
|
255
|
-
function noEmoji(val) {
|
|
256
|
-
// 限制表情:😀😂❤️🌟🎉🌍🐶☺
|
|
257
|
-
const regex = /[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E6}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{1F900}-\u{1F9FF}\u{263A}]+/gu
|
|
258
|
-
const newVal = val.replace(regex, '')
|
|
259
|
-
return newVal
|
|
260
|
-
}
|
|
261
|
-
</script>
|
|
262
|
-
|
|
263
|
-
注意事项
|
|
264
|
-
使用时需要传入limit,限制类型;当limit为float时,需要传入decimals,限制小数位数
|
|
265
|
-
支持插槽
|
|
266
|
-
使用
|
|
267
|
-
image.png
|
|
268
|
-
|
|
269
|
-
源码
|
|
270
|
-
www.npmjs.com/package/xtt…
|
|
271
|
-
|
|
272
|
-
/xtt-tools/directives-v2/input/index.js
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
function remove(arr, item) {
|
|
2
|
-
if (arr.length) {
|
|
3
|
-
var index = arr.indexOf(item)
|
|
4
|
-
if (index > -1) {
|
|
5
|
-
return arr.splice(index, 1)
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function getFirstComponentChild(children) {
|
|
11
|
-
if (Array.isArray(children)) {
|
|
12
|
-
for (var i = 0; i < children.length; i++) {
|
|
13
|
-
var c = children[i]
|
|
14
|
-
if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {
|
|
15
|
-
return c
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function isDef(v) {
|
|
21
|
-
return v !== undefined && v !== null
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function isAsyncPlaceholder(node) {
|
|
25
|
-
return node.isComment && node.asyncFactory
|
|
26
|
-
}
|
|
27
|
-
var patternTypes = [String, RegExp, Array]
|
|
28
|
-
|
|
29
|
-
export default {
|
|
30
|
-
name: 'keep-alive',
|
|
31
|
-
abstract: true,
|
|
32
|
-
computed: {
|
|
33
|
-
// 这里算是一个伪代码
|
|
34
|
-
// 缓存的数组 [{ 'tab1/组件名称':comp, 'tab2/组件名称':comp },{ 'tab1/组件名称':comp, 'tab2/组件名称':comp }]
|
|
35
|
-
cacheArray() {
|
|
36
|
-
return this.$store.state.xxx.groupCache
|
|
37
|
-
},
|
|
38
|
-
// 当前选中的分组 例:0/1/2... 这里用来读取cache数组的index
|
|
39
|
-
groupIndex() {
|
|
40
|
-
return this.$store.state.xxx.groupIndex
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
created: function created() {
|
|
45
|
-
// 当前tab的缓存
|
|
46
|
-
const cache = this.cacheArray[this.groupIndex]
|
|
47
|
-
this.cache = cache || Object.create(null)
|
|
48
|
-
// TODO 页面初始化事件,后续可触发初始化事件
|
|
49
|
-
},
|
|
50
|
-
destroyed: function destroyed(to, form) {
|
|
51
|
-
// TODO 页面离开事件,后续可触发关闭事件
|
|
52
|
-
},
|
|
53
|
-
render: function render() {
|
|
54
|
-
var slot = this.$slots.default
|
|
55
|
-
var vnode = getFirstComponentChild(slot)
|
|
56
|
-
var componentOptions = vnode && vnode.componentOptions
|
|
57
|
-
// check pattern
|
|
58
|
-
var ref$1 = this
|
|
59
|
-
var cache = ref$1.cache
|
|
60
|
-
const key = `${this.groupIndex}/${componentOptions.Ctor.options.name}`
|
|
61
|
-
// 存在key直接读取
|
|
62
|
-
if (cache[key]) {
|
|
63
|
-
vnode.componentInstance = cache[key].componentInstance
|
|
64
|
-
} else {
|
|
65
|
-
// 没有进行缓存
|
|
66
|
-
cache[key] = vnode
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 写入缓存
|
|
70
|
-
this.$store.dispatch('setGroupCache', {
|
|
71
|
-
cache: this.cache
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
return vnode || (slot && slot[0])
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
data(){
|
|
2
|
-
return {
|
|
3
|
-
bannerList:['11.png','222.png'],
|
|
4
|
-
swiperOption: {
|
|
5
|
-
pagination: {
|
|
6
|
-
el: ".case-swiper-pagination"
|
|
7
|
-
//区分不同Swiper的组件
|
|
8
|
-
},
|
|
9
|
-
//显示分页
|
|
10
|
-
loop: true,
|
|
11
|
-
//开启循环
|
|
12
|
-
loopedSlides: 6,
|
|
13
|
-
//设置平滑
|
|
14
|
-
slidesPerView: 3,
|
|
15
|
-
//设置能够同时显示的数量(可设置 auto)
|
|
16
|
-
speed: 1000,
|
|
17
|
-
//切换速度
|
|
18
|
-
|
|
19
|
-
navigation: {
|
|
20
|
-
prevEl: ".swiper-button-prev2",
|
|
21
|
-
nextEl: ".swiper-button-next2"
|
|
22
|
-
},
|
|
23
|
-
//左右箭头按钮(可自定义)
|
|
24
|
-
|
|
25
|
-
// autoplay: false,
|
|
26
|
-
//是否开启自动轮播
|
|
27
|
-
|
|
28
|
-
autoplay: {
|
|
29
|
-
delay: 1000,
|
|
30
|
-
stopOnLastSlide: false,
|
|
31
|
-
disableOnInteraction: false
|
|
32
|
-
},
|
|
33
|
-
// 开启自动轮播后,设置自动轮播的延迟时间
|
|
34
|
-
loopAdditionalSlides: 1,
|
|
35
|
-
//复制若干个slide
|
|
36
|
-
// slidesPerGroup: 3,
|
|
37
|
-
// 定义slides的数量多少为一组,即每次切换slides的数量,默认每次切换一张
|
|
38
|
-
on: {
|
|
39
|
-
slideChangeTransitionEnd: function() {
|
|
40
|
-
console.log(this.activeIndex);
|
|
41
|
-
//获取当前下标,切换结束时,告诉我现在是第几个slide
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
function longestRepeating(s: string, queryCharacters: string, queryIndices: number[]): number[] {
|
|
2
|
-
const n = s.length
|
|
3
|
-
const q = queryIndices.length
|
|
4
|
-
const res = Array(q).fill(1)
|
|
5
|
-
|
|
6
|
-
const odt = new ODT(n, -1) // !珂朵莉树维护区间字符类型
|
|
7
|
-
s.split('').forEach((c, i) => {
|
|
8
|
-
odt.set(i, i + 1, c.charCodeAt(0) - 97)
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
const lens = new SortedList<number>() // !SortedList维护每个连续段的长度
|
|
12
|
-
odt.enumerateAll((start, end, value) => {
|
|
13
|
-
if (value !== -1) {
|
|
14
|
-
lens.add(end - start)
|
|
15
|
-
}
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
for (let i = 0; i < q; i++) {
|
|
19
|
-
const target = queryCharacters.charCodeAt(i) - 97
|
|
20
|
-
const pos = queryIndices[i]
|
|
21
|
-
|
|
22
|
-
// !每次更新最多影响左中右三段区间
|
|
23
|
-
// !先删除这三段区间的长度,修改后,再添加这三段区间的长度
|
|
24
|
-
// 这种做法无需分类讨论
|
|
25
|
-
const [start, end] = odt.get(pos)!
|
|
26
|
-
const leftSeg = odt.get(start - 1)
|
|
27
|
-
const rightSeg = odt.get(end)
|
|
28
|
-
const first = leftSeg ? leftSeg[0] : 0
|
|
29
|
-
const last = rightSeg ? rightSeg[1] : n
|
|
30
|
-
odt.enumerateRange(first, last, (start, end) => {
|
|
31
|
-
lens.discard(end - start)
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
odt.set(pos, pos + 1, target)
|
|
35
|
-
|
|
36
|
-
odt.enumerateRange(first, last, (start, end) => {
|
|
37
|
-
lens.add(end - start)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
res[i] = lens.max()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return res
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
作者:草莓奶昔🍓
|
|
47
|
-
链接:https://leetcode.cn/problems/longest-substring-of-one-repeating-character/solutions/2287639/typescript-ke-duo-li-shu-by-981377660lmt-n77n/
|
|
48
|
-
来源:力扣(LeetCode)
|
|
49
|
-
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
|
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
// 方法一:放大画布之后,直接用 scale 放大整个坐标系
|
|
2
|
-
// * 但是你要知道我们一直是在放大的坐标系上绘制的,可能不知道什么时候(比如重新设置画布宽高),scale 可能就会被重置成 1 了,从而造成画面错乱
|
|
3
|
-
adaptDPR() { // 在初始化 canvas 的时候就要调用该方法
|
|
4
|
-
const dpr = window.devicePixelRatio;
|
|
5
|
-
const { width, height } = this.canvas;
|
|
6
|
-
// 重新设置 canvas 自身宽高大小和 css 大小。放大 canvas;css 保持不变,因为我们需要那么多的点
|
|
7
|
-
this.canvas.width = Math.round(width * dpr);
|
|
8
|
-
this.canvas.height = Math.round(height * dpr);
|
|
9
|
-
this.canvas.style.width = width + 'px';
|
|
10
|
-
this.canvas.style.height = height + 'px';
|
|
11
|
-
// 直接用 scale 放大整个坐标系,相对来说就是放大了每个绘制操作
|
|
12
|
-
this.ctx2d.scale(dpr, dpr);
|
|
13
|
-
// 接下来的绘制操作和往常一样,比如画个矩形 ctx2d.strokeRect(x, y, w, h);原来该怎么算就怎么算,该怎么调用还是怎么调用
|
|
14
|
-
}
|
|
15
|
-
// 方法二:放大画布之后,需要把每一个绘制的 api 都乘以 dpr
|
|
16
|
-
// * 这样一来使用的时候就会很麻烦,所以我们需要把所有的绘制操作进行统一封装
|
|
17
|
-
// 可以参考这个库:https://github.com/jondavidjohn/hidpi-canvas-polyfill,不过这个库也不是所有 api 都覆盖
|
|
18
|
-
adaptDPR() { // 在初始化 canvas 的时候就要调用该方法
|
|
19
|
-
const dpr = window.devicePixelRatio;
|
|
20
|
-
const { width, height } = this.canvas;
|
|
21
|
-
// 重新设置 canvas 自身宽高大小和 css 大小。放大 canvas;css 保持不变,因为我们需要那么多的点
|
|
22
|
-
this.canvas.width = Math.round(width * dpr);
|
|
23
|
-
this.canvas.height = Math.round(height * dpr);
|
|
24
|
-
this.canvas.style.width = width + 'px';
|
|
25
|
-
this.canvas.style.height = height + 'px';
|
|
26
|
-
// 注意这里没有用 scale
|
|
27
|
-
}
|
|
28
|
-
// 接下来在每个涉及绘制的 api 时都乘以 dpr,比如绘制矩形的时候
|
|
29
|
-
strokeRect(x: number, y: number, w: number, h: number) {
|
|
30
|
-
const { ctx2d, dpr } = this;
|
|
31
|
-
if (!ctx2d) return;
|
|
32
|
-
x = x * dpr;
|
|
33
|
-
y = y * dpr;
|
|
34
|
-
w = w * dpr;
|
|
35
|
-
h = h * dpr;
|
|
36
|
-
ctx2d.strokeRect(x, y, w, h);
|
|
37
|
-
}
|
|
38
|
-
function createHDCanvas (canvas, w, h) {
|
|
39
|
-
const ratio = window.devicePixelRatio || 1;
|
|
40
|
-
canvas.width = w * ratio; // 实际渲染像素
|
|
41
|
-
canvas.height = h * ratio; // 实际渲染像素
|
|
42
|
-
canvas.style.width = `${w}px`; // 控制显示大小
|
|
43
|
-
canvas.style.height = `${h}px`; // 控制显示大小
|
|
44
|
-
const ctx = canvas.getContext('2d')
|
|
45
|
-
ctx.scale(ratio, ratio)
|
|
46
|
-
// canvas 绘制
|
|
47
|
-
return canvas;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
作者:李彦辉Jacky
|
|
51
|
-
链接:https://juejin.cn/post/7014765000916992036
|
|
52
|
-
来源:稀土掘金
|
|
53
|
-
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
|
|
54
|
-
|
|
55
|
-
const TTL = 5 * 1000 * 1000
|
|
56
|
-
const startTime = new Date().getTime()
|
|
57
|
-
async function polling() {
|
|
58
|
-
|
|
59
|
-
// 超过存活时间结束
|
|
60
|
-
const endTime = new Date().getTime()
|
|
61
|
-
if (endTime - startTime > TTL) {
|
|
62
|
-
return
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// 这里的remoteCall是一个虚拟的函数,指代需要轮询的http接口
|
|
66
|
-
const response = await remoteCall()
|
|
67
|
-
|
|
68
|
-
// 这里的状态可以基于http,也可以自己定义自己的结构来处理。这里用200和非200做一个事例
|
|
69
|
-
if (response.status === 200) {
|
|
70
|
-
return response
|
|
71
|
-
} else {
|
|
72
|
-
// 这里可以看情况设定一个间隔时间。
|
|
73
|
-
// 复杂一点的可以设定一些间隔时间策略: 比如根据请求次数增加,增加间隔时间等等
|
|
74
|
-
// 这里只是一个例子,等待5秒
|
|
75
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
76
|
-
return await polling()
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
作者:小南聊一聊
|
|
81
|
-
链接:https://juejin.cn/post/7099252918682910733
|
|
82
|
-
来源:稀土掘金
|
|
83
|
-
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
|
|
84
|
-
|
|
85
|
-
.ui-input[is-error], [is="ui-input"][is-error], .ui-input > [is-error] {
|
|
86
|
-
border-color: var(--ui-red, #eb4646) !important;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
input.ui-input, .ui-input > input, [is="ui-input"] {
|
|
90
|
-
height: var(--ui-input-height);
|
|
91
|
-
line-height: var(--ui-input-line-height);
|
|
92
|
-
padding: calc((var(--ui-input-height) - var(--ui-input-line-height) - 2px) / 2) 8px;
|
|
93
|
-
border: 1px solid var(--ui-border, #d0d0d5);
|
|
94
|
-
border-radius: var(--ui-radius, 4px);
|
|
95
|
-
background-color: var(--ui-white, #fff);
|
|
96
|
-
box-sizing: border-box;
|
|
97
|
-
font-size: var(--ui-font, 14px);
|
|
98
|
-
outline: none;
|
|
99
|
-
color: var(--ui-dark, #4c5161);
|
|
100
|
-
transition: border-color var(--ui-animate-time, .2s), background-color var(--ui-animate-time, .2s);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
import mitt from 'mitt'
|
|
104
|
-
|
|
105
|
-
// 当前文件名 需要同步修改
|
|
106
|
-
const CURRENT_FILE_NAME = 'useMitt'
|
|
107
|
-
|
|
108
|
-
// 基础配置
|
|
109
|
-
const defineOptions = {
|
|
110
|
-
// 是否禁用
|
|
111
|
-
logDisabled: false,
|
|
112
|
-
// 是否展开
|
|
113
|
-
logExpanded: false,
|
|
114
|
-
// console.group样式
|
|
115
|
-
logStyle: 'color: #fff; background: #51a2e4; font-size: 12px; padding: 4px; border-radius: 4px'
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// uuid
|
|
119
|
-
let idCounter = 0;
|
|
120
|
-
const uniqueId = (prefix) => {
|
|
121
|
-
return prefix.toString() + ++idCounter;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// 操作时间 HH:mm:ss:ms
|
|
125
|
-
const formatTime = (date = new Date()) => {
|
|
126
|
-
const hours = date.getHours().toString().padStart(2, '0');
|
|
127
|
-
const minutes = date.getMinutes().toString().padStart(2, '0');
|
|
128
|
-
const seconds = date.getSeconds().toString().padStart(2, '0');
|
|
129
|
-
const milliseconds = date.getMilliseconds().toString();
|
|
130
|
-
|
|
131
|
-
return `${hours}:${minutes}:${seconds}:${milliseconds}`;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// 获取堆栈
|
|
135
|
-
const getStackTrace = () => {
|
|
136
|
-
let stackStr = ''
|
|
137
|
-
let stackArr = []
|
|
138
|
-
// 通过Error获取
|
|
139
|
-
try {
|
|
140
|
-
throw new Error('');
|
|
141
|
-
}
|
|
142
|
-
// catch掉才不会被博睿监控到
|
|
143
|
-
catch (error) {
|
|
144
|
-
stackStr = error.stack || '';
|
|
145
|
-
}
|
|
146
|
-
// 字符串信息分割为数组 并去掉空格
|
|
147
|
-
stackArr = stackStr.split('\n').map((line) => line.trim());
|
|
148
|
-
return stackArr.splice(stackArr[0] === 'Error' ? 2 : 1);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// 在堆栈信息数组中 匹配触发事件的函数
|
|
152
|
-
const matchFnCaller = (stackTrace = []) => {
|
|
153
|
-
// 文件在src下的 且排除本文件 没有匹配的返回第0项
|
|
154
|
-
return stackTrace.find(item => item.includes('src') && !item.includes(CURRENT_FILE_NAME)) || stackTrace[0]
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
// 事件信息map结构 key: eventName value: { count: 触发次数, describe: 事件描述 }
|
|
159
|
-
const eventMap = new Map()
|
|
160
|
-
|
|
161
|
-
// 监听事件 配置logger
|
|
162
|
-
const handleOnEvent = (eventName, value, options) => {
|
|
163
|
-
const stackTrace = getStackTrace()
|
|
164
|
-
|
|
165
|
-
// 匹配事件源 去掉 at 字符
|
|
166
|
-
const source = matchFnCaller(stackTrace).substring(3)
|
|
167
|
-
const describe = eventMap.get(eventName).describe
|
|
168
|
-
const count = eventMap.get(eventName).count
|
|
169
|
-
|
|
170
|
-
const g = options.logExpanded ? console.group : console.groupCollapsed
|
|
171
|
-
g(`%cmittId => ${options.id}`, options.logStyle)
|
|
172
|
-
console.log('eventName:', eventName)
|
|
173
|
-
console.log('eventValue:', value)
|
|
174
|
-
console.log('eventDescribe:', describe)
|
|
175
|
-
console.log('eventSource:', source)
|
|
176
|
-
console.log('count:', count)
|
|
177
|
-
console.log('time:', formatTime())
|
|
178
|
-
console.groupEnd()
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// 记录事件信息
|
|
182
|
-
const markEventMap = (type, describe) => {
|
|
183
|
-
if (eventMap.has(type)) {
|
|
184
|
-
let event = eventMap.get(type)
|
|
185
|
-
eventMap.set(type, {
|
|
186
|
-
count: ++event.count,
|
|
187
|
-
describe
|
|
188
|
-
})
|
|
189
|
-
} else {
|
|
190
|
-
eventMap.set(type, {
|
|
191
|
-
count: 1,
|
|
192
|
-
describe
|
|
193
|
-
})
|
|
194
|
-
}
|
|
195
|
-
console.log('eventMap', eventMap)
|
|
196
|
-
}
|
|
197
|
-
// emit事件 记录事件信息
|
|
198
|
-
const handleEmitEvent = (emitter) => {
|
|
199
|
-
const originEmit = emitter.emit
|
|
200
|
-
emitter.emit = (type, e, describe = '') => {
|
|
201
|
-
markEventMap(type, describe)
|
|
202
|
-
originEmit(type, e)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// 增加emitAsync方法
|
|
207
|
-
// https://github.com/developit/mitt/discussions/157
|
|
208
|
-
const emitAsync = async function(type, e, describe) {
|
|
209
|
-
// 记录事件信息
|
|
210
|
-
markEventMap(type, describe)
|
|
211
|
-
|
|
212
|
-
let handlers = this.all.get(type)
|
|
213
|
-
if (handlers) {
|
|
214
|
-
for (const f of handlers) {
|
|
215
|
-
await f(e)
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
handlers = this.all.get('*')
|
|
219
|
-
if (handlers) {
|
|
220
|
-
for (const f of handlers) {
|
|
221
|
-
await f(type, e)
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
const mittAsync = (all) => {
|
|
226
|
-
const instance = mitt(all)
|
|
227
|
-
instance.emitAsync = emitAsync
|
|
228
|
-
return instance
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export default function useMitt(customOption) {
|
|
232
|
-
// 配置项
|
|
233
|
-
const options = Object.assign({
|
|
234
|
-
id: uniqueId('mitt-')
|
|
235
|
-
}, defineOptions, customOption)
|
|
236
|
-
|
|
237
|
-
// 实例化mitt
|
|
238
|
-
const emitter = mittAsync()
|
|
239
|
-
|
|
240
|
-
if (!options.disabled) {
|
|
241
|
-
// 监听所有事件,配置logger
|
|
242
|
-
emitter.on('*', (eventName, value) => handleOnEvent(eventName, value, options))
|
|
243
|
-
// 劫持emit,记录信息
|
|
244
|
-
handleEmitEvent(emitter)
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return emitter
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
import useMitt from '@/hooks/useMitt'
|
|
251
|
-
|
|
252
|
-
const emitter = useMitt({id: 'listEmitter', logExpanded: true})
|
|
253
|
-
const handleClick = () => {
|
|
254
|
-
emitter.emitAsync('showModal', 'data', '此函数是激活confirm modal')
|
|
255
|
-
.then(() => {
|
|
256
|
-
console.log('confirm') // resolve后打印
|
|
257
|
-
})
|
|
258
|
-
.catch(() => {
|
|
259
|
-
console.log('cancel') // reject后打印
|
|
260
|
-
})
|
|
261
|
-
}
|
|
262
|
-
emitter.on('showModal', () => {
|
|
263
|
-
return new Promise((resolve, reject) => {
|
|
264
|
-
Modal.confirm({
|
|
265
|
-
title: '确认是否XXX',
|
|
266
|
-
onOk() {
|
|
267
|
-
resolve()
|
|
268
|
-
},
|
|
269
|
-
onCancel() {
|
|
270
|
-
reject()
|
|
271
|
-
}
|
|
272
|
-
})
|
|
273
|
-
})
|
|
274
|
-
})
|
|
275
|
-
import useMitt from '@/hooks/useMitt'
|
|
276
|
-
const emitter = useMitt({ id: 'contract-edit', logExpanded: true })
|
|
277
|
-
const handleClick = () => {
|
|
278
|
-
emitter.emit('click', 'data', '这是一个点击事件')
|
|
279
|
-
}
|
|
280
|
-
export default function mitt(all){
|
|
281
|
-
//使用Map存储注册的事件
|
|
282
|
-
all = all || new Map();
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
return {
|
|
286
|
-
all,
|
|
287
|
-
on(type, handler, priority = 0, listener) {
|
|
288
|
-
const handlers = all!.get(type) || [];
|
|
289
|
-
handlers.push({handler, priority});
|
|
290
|
-
handlers.sort((a, b) => b.priority - a.priority);
|
|
291
|
-
all!.set(type, handlers);
|
|
292
|
-
if (listener) {
|
|
293
|
-
listener({type, handler, priority});
|
|
294
|
-
}
|
|
295
|
-
},
|
|
296
|
-
|
|
297
|
-
off(type, handler) {
|
|
298
|
-
const handlers = all!.get(type);
|
|
299
|
-
if (handlers) {
|
|
300
|
-
if (handler) {
|
|
301
|
-
const index = handlers.findIndex(item => item.handler === handler);
|
|
302
|
-
handlers.splice(index > -1 ? index : 0, 1);
|
|
303
|
-
}
|
|
304
|
-
else {
|
|
305
|
-
all!.set(type, []);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
},
|
|
309
|
-
|
|
310
|
-
emit(type, evt, listener) {
|
|
311
|
-
if (listener) {
|
|
312
|
-
listener({type, evt});
|
|
313
|
-
}
|
|
314
|
-
let handlers = all!.get(type);
|
|
315
|
-
if (handlers) {
|
|
316
|
-
handlers.slice().map(async (handler) => {
|
|
317
|
-
await handler(evt);
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
function eventListener({type, handler, priority}) {
|
|
326
|
-
console.log(`事件 ${type} 已注册,处理函数为 ${handler},优先级为 ${priority}`);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
function eventTriggerListener({type, evt}) {
|
|
331
|
-
console.log(`事件 ${type} 已触发,事件对象为 ${evt}`);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const bus = mitt();
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
bus.on('click', () => console.log('点击事件已触发'), 1, eventListener);
|
|
339
|
-
bus.emit('click', {target: '按钮'}, eventTriggerListener);
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const pages = getCurrentPages();
|
|
343
|
-
const prePage = pages[pages.length - 1].__displayReporter.showReferpagepath;
|
|
344
|
-
if(prePage === "pages/scan_pay/index.html"){
|
|
345
|
-
wx.reLaunch({
|
|
346
|
-
url:"../index/index"
|
|
347
|
-
});
|
|
348
|
-
return
|
|
349
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
最近开发小程序期间,有一个较大的表单页面,用户编辑期间若误触返回键或误触发滑动返回,再次进入页面表单数据会丢失,当然可以实现记住表单状态,但会增加代码复杂度,感觉不够优雅。遂去搜索小程序返回拦截的实现,大部分文章并没有很好的达到效果,找到了一个个人感觉还较好的实现,微信小程序 / UNIAPP --- 阻止小程序返回(顶部导航栏返回、左 / 右滑手势、安卓物理返回键和调用 navigateBack 接口) - 简书,但感觉还是不符合我的需求,我想要实现类似我们在退出一些app首页时会弹出一个toast那种效果,于是我参考以上代码做了如下实现,也是封装为了一个组件。
|
|
2
|
-
代码实现
|
|
3
|
-
html 代码解读复制代码<!--components/back-confirmation/back-confirmation.wxml-->
|
|
4
|
-
<page-container show="{{show}}" overlay="{{false}}" bind:leave="leave" message="{{message}}"> </page-container>
|
|
5
|
-
|
|
6
|
-
首先页面代码就是一个无显示无遮罩的page-container,这样我们把这个组件引入到页面中时对显示没有任何影响。新加了一个message属性用于自定义toast消息。
|
|
7
|
-
javascript 代码解读复制代码// components/back-confirmation/back-confirmation.js
|
|
8
|
-
let interval = null
|
|
9
|
-
|
|
10
|
-
Component({
|
|
11
|
-
properties: {
|
|
12
|
-
show: {
|
|
13
|
-
type: Boolean,
|
|
14
|
-
value: false
|
|
15
|
-
},
|
|
16
|
-
message: {
|
|
17
|
-
type: String,
|
|
18
|
-
value: '再按一次退出编辑'
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
data: {},
|
|
23
|
-
|
|
24
|
-
lifetimes: {
|
|
25
|
-
detached() {
|
|
26
|
-
if (interval) {
|
|
27
|
-
clearInterval(interval)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
observers: {
|
|
33
|
-
'show': function (show) {
|
|
34
|
-
if (!show) {
|
|
35
|
-
if (interval) {
|
|
36
|
-
clearInterval(interval)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
methods: {
|
|
43
|
-
async leave() {
|
|
44
|
-
if (interval) {
|
|
45
|
-
clearInterval(interval)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (this.data.show) {
|
|
49
|
-
await wx.showToast({
|
|
50
|
-
title: this.data.message,
|
|
51
|
-
icon: 'none',
|
|
52
|
-
})
|
|
53
|
-
interval = setInterval(() => {
|
|
54
|
-
this.setData({
|
|
55
|
-
show: true
|
|
56
|
-
})
|
|
57
|
-
}, 2000)
|
|
58
|
-
} else {
|
|
59
|
-
await wx.navigateBack()
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
对比参考文章,js代码的主要区别就是弹窗改为了toast,另外加了一个interval,如果用户只是误触,不进行二次确认则会在一定时长后重新恢复为需要二次确认。另外我对取消show加了一个observer,有什么作用在后面应用时再讲。
|
|
66
|
-
使用方法
|
|
67
|
-
html 代码解读复制代码<back-confirmation show="{{useBackConfirmation}}"/>
|
|
68
|
-
|
|
69
|
-
把useBackConfirmation的值设为true,一个退出页面的二次确认就实现了,使用效果如下,在第一下滑动返回时弹出toast消息,2秒内再次滑动才会退出页面,超过两秒则会恢复原状态,需要重新二次确认。
|
|
70
|
-
|
|
71
|
-
滑动返回是一个比较容易误触发的操作,但若我们的程序有顶部导航栏,顶部导航栏则相对很少误触,那我们希望若是点击导航栏就直接退出。但是我们在代码调用wx.navigateBack()时还是会被我们的二次确认拦截,这就需要先手动把组件的show值关闭,然后在调用wx.navigateBack(),也就是如下代码
|
|
72
|
-
javascript 代码解读复制代码async goBack() {
|
|
73
|
-
this.setData({
|
|
74
|
-
useBackConfirmation: false,
|
|
75
|
-
})
|
|
76
|
-
// 立即退出无效
|
|
77
|
-
setTimeout(() => {
|
|
78
|
-
wx.navigateBack()
|
|
79
|
-
}, 10)
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
在返回按钮上绑定该方法,改变show值时前面提到的observer会把interval关掉,避免手动改变后马上又被恢复的情况出现,另外set值后等一定时间再调用wx.navigateBack(),不然可能会有bug。
|
|
83
|
-
需要注意不要在小程序首页使用该封装后的组件,不符合小程序开发规范,另外因为首页本身是无法调用wx.navigateBack()的,我也没有测试到底可不可用。
|
|
84
|
-
理解page-container的原理
|
|
85
|
-
page-container组件的所有属性,最重要的是show值。在页面上引入这个组件后,若show值为true,页面上所有各种方式触发的返回操作都会被这个组件所拦截,然后自动将值置为false。当值为false后,这个组件就没有作用了,但是我们可以重新赋值,就能让它重新恢复拦截。
|
|
86
|
-
在官方文档中,示例代码在page-container中是有具体组件的,这可能误导我们忽略了让它去实现返回二次确认这种无任何显示的最纯粹的功能。当然,基于page-container其实还能实现各种复杂的覆盖在页面之上的组件。
|
|
87
|
-
写在最后
|
|
88
|
-
以上是我在互联网发布的第一个技术分享,其实比较简单,但因为在网上没能找到比较好的方案,所以选择了发布出来。最近开发中其实还遇到了很多问题,本来还想自己建个博客的,但是实在是没有时间,就先写这一篇发布在掘金上了,希望能帮助到一些人。 标签: 微信小程序 评论 0 0 / 1000
|
|
89
|
-
标点符号、链接等不计算在有效字数内
|
|
90
|
-
|
|
91
|
-
Ctrl + Enter
|
|
92
|
-
|
|
93
|
-
发送
|
|
94
|
-
暂无评论数据
|
|
95
|
-
3
|
|
96
|
-
评论
|
|
97
|
-
收藏
|
|
98
|
-
加个关注,精彩更新不错过~
|
|
99
|
-
关注
|
|
100
|
-
|
|
101
|
-
雨润田上
|
|
102
|
-
|
|
103
|
-
1
|
|
104
|
-
文章
|
|
105
|
-
821
|
|
106
|
-
阅读
|
|
107
|
-
0
|
|
108
|
-
粉丝 加个关注,精彩更新不错过~
|
|
109
|
-
|
|
110
|
-
作者:雨润田上
|
|
111
|
-
链接:https://juejin.cn/post/7456058116972478474
|
|
112
|
-
来源:稀土掘金
|
|
113
|
-
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
|