@10yun/cv-mobile-ui 0.5.31 → 0.5.32
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/package.json +1 -1
- package/uview-plus/components/u-copy/u-copy.vue +68 -1
- package/uview-plus/components/u-form/u-form.vue +243 -1
- package/uview-plus/components/u-qrcode/u-qrcode.vue +275 -3
- package/uview-plus/libs/ctocode/image.js +17 -0
- package/uview-plus/libs/ctocode/index.js +34 -4
- package/uview-plus/libs/ctocode/map.js +38 -0
- package/uview-plus/libs/function/index-wu.js +5 -0
package/package.json
CHANGED
|
@@ -1 +1,68 @@
|
|
|
1
|
-
<template>
|
|
2
1
|
<view @click="handleClick">
|
|
3
2
|
<slot>复制</slot>
|
|
4
3
|
</view>
|
|
5
4
|
name: 'up-copy',
|
|
6
5
|
props: {
|
|
7
6
|
content: {
|
|
8
7
|
type: String,
|
|
9
8
|
default: ''
|
|
10
9
|
},
|
|
11
10
|
alertStyle: {
|
|
12
11
|
type: String,
|
|
13
12
|
default: 'toast'
|
|
14
13
|
},
|
|
15
14
|
notice: {
|
|
16
15
|
type: String,
|
|
17
16
|
default: '复制成功'
|
|
18
17
|
}
|
|
19
18
|
},
|
|
20
19
|
emits: ['success'],
|
|
21
20
|
methods: {
|
|
22
21
|
handleClick() {
|
|
23
22
|
let content = this.content;
|
|
24
23
|
if (!content) {
|
|
25
24
|
uni.showToast({
|
|
26
25
|
title: '暂无',
|
|
27
26
|
icon: 'none',
|
|
28
27
|
duration: 2000
|
|
29
28
|
});
|
|
30
29
|
return false;
|
|
31
30
|
}
|
|
32
31
|
content = typeof content === 'string' ? content : content.toString(); // 复制内容,必须字符串,数字需要转换为字符串
|
|
33
32
|
/**
|
|
34
33
|
* 小程序端 和 app端的复制逻辑
|
|
35
34
|
*/
|
|
36
35
|
let that = this;
|
|
37
36
|
uni.setClipboardData({
|
|
38
37
|
data: content,
|
|
39
38
|
success: function () {
|
|
40
39
|
if (that.alertStyle == 'modal') {
|
|
41
40
|
uni.showModal({
|
|
42
41
|
title: '提示',
|
|
43
42
|
content: that.notice
|
|
44
43
|
});
|
|
45
44
|
} else {
|
|
46
45
|
uni.showToast({
|
|
47
46
|
title: that.notice,
|
|
48
47
|
icon: 'none'
|
|
49
48
|
});
|
|
50
49
|
}
|
|
51
50
|
that.$emit('success');
|
|
52
51
|
},
|
|
53
52
|
fail: function () {
|
|
54
53
|
uni.showToast({
|
|
55
54
|
title: '复制失败',
|
|
56
55
|
icon: 'none',
|
|
57
56
|
duration: 3000
|
|
58
57
|
});
|
|
59
58
|
}
|
|
60
59
|
});
|
|
61
60
|
}
|
|
62
61
|
}
|
|
62
|
+
<template>
|
|
63
|
+
<view @click="handleClick">
|
|
64
|
+
<slot>复制</slot>
|
|
65
|
+
</view>
|
|
66
|
+
</template>
|
|
67
|
+
<script>
|
|
68
|
+
export default {
|
|
69
|
+
name: 'up-copy',
|
|
70
|
+
props: {
|
|
71
|
+
content: {
|
|
72
|
+
type: String,
|
|
73
|
+
default: ''
|
|
74
|
+
},
|
|
75
|
+
alertStyle: {
|
|
76
|
+
type: String,
|
|
77
|
+
default: 'toast'
|
|
78
|
+
},
|
|
79
|
+
notice: {
|
|
80
|
+
type: String,
|
|
81
|
+
default: '复制成功'
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
emits: ['success'],
|
|
85
|
+
methods: {
|
|
86
|
+
handleClick() {
|
|
87
|
+
let content = this.content;
|
|
88
|
+
if (!content) {
|
|
89
|
+
uni.showToast({
|
|
90
|
+
title: '暂无',
|
|
91
|
+
icon: 'none',
|
|
92
|
+
duration: 2000
|
|
93
|
+
});
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
content = typeof content === 'string' ? content : content.toString(); // 复制内容,必须字符串,数字需要转换为字符串
|
|
97
|
+
/**
|
|
98
|
+
* 小程序端 和 app端的复制逻辑
|
|
99
|
+
*/
|
|
100
|
+
let that = this;
|
|
101
|
+
uni.setClipboardData({
|
|
102
|
+
data: content,
|
|
103
|
+
success: function () {
|
|
104
|
+
if (that.alertStyle == 'modal') {
|
|
105
|
+
uni.showModal({
|
|
106
|
+
title: '提示',
|
|
107
|
+
content: that.notice
|
|
108
|
+
});
|
|
109
|
+
} else {
|
|
110
|
+
uni.showToast({
|
|
111
|
+
title: that.notice,
|
|
112
|
+
icon: 'none'
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
that.$emit('success');
|
|
116
|
+
},
|
|
117
|
+
fail: function () {
|
|
118
|
+
uni.showToast({
|
|
119
|
+
title: '复制失败',
|
|
120
|
+
icon: 'none',
|
|
121
|
+
duration: 3000
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
</script>
|
|
129
|
+
<style scoped></style>
|
|
@@ -1 +1,243 @@
|
|
|
1
|
-
<template>
|
|
2
1
|
<view class="u-form">
|
|
3
2
|
<slot />
|
|
4
3
|
</view>
|
|
5
4
|
* Form 表单
|
|
6
5
|
* @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
|
|
7
6
|
* @tutorial https://ijry.github.io/uview-plus/components/form.html
|
|
8
7
|
* @property {Object} model 当前form的需要验证字段的集合
|
|
9
8
|
* @property {Object | Function | Array} rules 验证规则
|
|
10
9
|
* @property {String} errorType 错误的提示方式,见上方说明 ( 默认 message )
|
|
11
10
|
* @property {Boolean} borderBottom 是否显示表单域的下划线边框 ( 默认 true )
|
|
12
11
|
* @property {String} labelPosition 表单域提示文字的位置,left-左侧,top-上方 ( 默认 'left' )
|
|
13
12
|
* @property {String | Number} labelWidth 提示文字的宽度,单位px ( 默认 45 )
|
|
14
13
|
* @property {String} labelAlign lable字体的对齐方式 ( 默认 ‘left' )
|
|
15
14
|
* @property {Object} labelStyle lable的样式,对象形式
|
|
16
15
|
* @example <up-formlabelPosition="left" :model="model1" :rules="rules" ref="form1"></up-form>
|
|
17
16
|
*/
|
|
18
17
|
name: 'u-form',
|
|
19
18
|
mixins: [mpMixin, mixin, props],
|
|
20
19
|
provide() {
|
|
21
20
|
return {
|
|
22
21
|
uForm: this
|
|
23
22
|
};
|
|
24
23
|
},
|
|
25
24
|
data() {
|
|
26
25
|
return {
|
|
27
26
|
formRules: {},
|
|
28
27
|
// 规则校验器
|
|
29
28
|
validator: {},
|
|
30
29
|
// 原始的model快照,用于resetFields方法重置表单时使用
|
|
31
30
|
originalModel: null
|
|
32
31
|
};
|
|
33
32
|
},
|
|
34
33
|
watch: {
|
|
35
34
|
// 监听规则的变化
|
|
36
35
|
rules: {
|
|
37
36
|
immediate: true,
|
|
38
37
|
handler(n) {
|
|
39
38
|
this.setRules(n);
|
|
40
39
|
}
|
|
41
40
|
},
|
|
42
41
|
// 监听属性的变化,通知子组件u-form-item重新获取信息
|
|
43
42
|
propsChange(n) {
|
|
44
43
|
if (this.children?.length) {
|
|
45
44
|
this.children.map((child) => {
|
|
46
45
|
// 判断子组件(u-form-item)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值)
|
|
47
46
|
typeof child.updateParentData == 'function' && child.updateParentData();
|
|
48
47
|
});
|
|
49
48
|
}
|
|
50
49
|
},
|
|
51
50
|
// 监听model的初始值作为重置表单的快照
|
|
52
51
|
model: {
|
|
53
52
|
immediate: true,
|
|
54
53
|
handler(n) {
|
|
55
54
|
if (!this.originalModel) {
|
|
56
55
|
this.originalModel = deepClone(n);
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
58
|
}
|
|
60
59
|
},
|
|
61
60
|
computed: {
|
|
62
61
|
propsChange() {
|
|
63
62
|
return [this.errorType, this.borderBottom, this.labelPosition, this.labelWidth, this.labelAlign, this.labelStyle];
|
|
64
63
|
}
|
|
65
64
|
},
|
|
66
65
|
created() {
|
|
67
66
|
// 存储当前form下的所有u-form-item的实例
|
|
68
67
|
// 不能定义在data中,否则微信小程序会造成循环引用而报错
|
|
69
68
|
this.children = [];
|
|
70
69
|
},
|
|
71
70
|
methods: {
|
|
72
71
|
// 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则
|
|
73
72
|
setRules(rules) {
|
|
74
73
|
// 判断是否有规则
|
|
75
74
|
if (Object.keys(rules).length === 0) return;
|
|
76
75
|
if (process.env.NODE_ENV === 'development' && Object.keys(this.model).length === 0) {
|
|
77
76
|
error('设置rules,model必须设置!如果已经设置,请刷新页面。');
|
|
78
77
|
return;
|
|
79
78
|
}
|
|
80
79
|
this.formRules = rules;
|
|
81
80
|
// 重新将规则赋予Validator
|
|
82
81
|
this.validator = new Schema(rules);
|
|
83
82
|
},
|
|
84
83
|
// 清空所有u-form-item组件的内容,本质上是调用了u-form-item组件中的resetField()方法
|
|
85
84
|
resetFields() {
|
|
86
85
|
this.resetModel();
|
|
87
86
|
},
|
|
88
87
|
// 重置model为初始值的快照
|
|
89
88
|
resetModel(obj) {
|
|
90
89
|
// 历遍所有u-form-item,根据其prop属性,还原model的原始快照
|
|
91
90
|
this.children.map((child) => {
|
|
92
91
|
const prop = child?.prop;
|
|
93
92
|
const value = getProperty(this.originalModel, prop);
|
|
94
93
|
setProperty(this.model, prop, value);
|
|
95
94
|
});
|
|
96
95
|
},
|
|
97
96
|
// 清空校验结果
|
|
98
97
|
clearValidate(props) {
|
|
99
98
|
props = [].concat(props);
|
|
100
99
|
this.children.map((child) => {
|
|
101
100
|
// 如果u-form-item的prop在props数组中,则清除对应的校验结果信息
|
|
102
101
|
if (props[0] === undefined || props.includes(child.prop)) {
|
|
103
102
|
child.message = null;
|
|
104
103
|
}
|
|
105
104
|
});
|
|
106
105
|
},
|
|
107
106
|
// 对部分表单字段进行校验
|
|
108
107
|
async validateField(value, callback, event = null, options) {
|
|
109
108
|
// $nextTick是必须的,否则model的变更,可能会延后于此方法的执行
|
|
110
109
|
this.$nextTick(() => {
|
|
111
110
|
// 校验错误信息,返回给回调方法,用于存放所有form-item的错误信息
|
|
112
111
|
const errorsRes = [];
|
|
113
112
|
// 如果为字符串,转为数组
|
|
114
113
|
value = [].concat(value);
|
|
115
114
|
// 历遍children所有子form-item
|
|
116
115
|
let promises = this.children.map((child) => {
|
|
117
116
|
return new Promise((resolve, reject) => {
|
|
118
117
|
// 用于存放form-item的错误信息
|
|
119
118
|
const childErrors = [];
|
|
120
119
|
if (value.includes(child.prop)) {
|
|
121
120
|
// 获取对应的属性,通过类似'a.b.c'的形式
|
|
122
121
|
const propertyVal = getProperty(this.model, child.prop);
|
|
123
122
|
// 属性链数组
|
|
124
123
|
const propertyChain = child.prop.split('.');
|
|
125
124
|
const propertyName = propertyChain[propertyChain.length - 1];
|
|
126
125
|
let rule = [];
|
|
127
126
|
if (child.itemRules && child.itemRules.length > 0) {
|
|
128
127
|
rule = child.itemRules;
|
|
129
128
|
} else {
|
|
130
129
|
rule = this.formRules[child.prop];
|
|
131
130
|
}
|
|
132
131
|
// 如果不存在对应的规则,直接返回,否则校验器会报错
|
|
133
132
|
if (!rule) {
|
|
134
133
|
resolve();
|
|
135
134
|
return;
|
|
136
135
|
}
|
|
137
136
|
// rule规则可为数组形式,也可为对象形式,此处拼接成为数组
|
|
138
137
|
const rules = [].concat(rule);
|
|
139
138
|
// 对rules数组进行校验
|
|
140
139
|
if (!rules.length) {
|
|
141
140
|
resolve();
|
|
142
141
|
}
|
|
143
142
|
for (let i = 0; i < rules.length; i++) {
|
|
144
143
|
const ruleItem = rules[i];
|
|
145
144
|
// 将u-form-item的触发器转为数组形式
|
|
146
145
|
const trigger = [].concat(ruleItem?.trigger);
|
|
147
146
|
// 如果是有传入触发事件,但是此form-item却没有配置此触发器的话,不执行校验操作
|
|
148
147
|
if (event && !trigger.includes(event)) {
|
|
149
148
|
resolve();
|
|
150
149
|
continue;
|
|
151
150
|
}
|
|
152
151
|
// 实例化校验对象,传入构造规则
|
|
153
152
|
const validator = new Schema({
|
|
154
153
|
[propertyName]: ruleItem
|
|
155
154
|
});
|
|
156
155
|
validator.validate(
|
|
157
156
|
{
|
|
158
157
|
[propertyName]: propertyVal
|
|
159
158
|
},
|
|
160
159
|
(errors, fields) => {
|
|
161
160
|
if (test.array(errors)) {
|
|
162
161
|
errors.forEach((element) => {
|
|
163
162
|
element.prop = child.prop;
|
|
164
163
|
});
|
|
165
164
|
errorsRes.push(...errors);
|
|
166
165
|
childErrors.push(...errors);
|
|
167
166
|
}
|
|
168
167
|
//没有配置,或者配置了showErrorMsg为true时候,才修改子组件message,默认没有配置
|
|
169
168
|
if (!options || options?.showErrorMsg == true) {
|
|
170
169
|
child.message = childErrors[0]?.message ? childErrors[0].message : null;
|
|
171
170
|
}
|
|
172
171
|
if (i == rules.length - 1) {
|
|
173
172
|
resolve(errorsRes);
|
|
174
173
|
}
|
|
175
174
|
}
|
|
176
175
|
);
|
|
177
176
|
}
|
|
178
177
|
} else {
|
|
179
178
|
resolve({});
|
|
180
179
|
}
|
|
181
180
|
});
|
|
182
181
|
});
|
|
183
182
|
// 使用Promise.all来等待所有Promise完成
|
|
184
183
|
Promise.all(promises)
|
|
185
184
|
.then((results) => {
|
|
186
185
|
// 执行回调函数
|
|
187
186
|
typeof callback === 'function' && callback(errorsRes);
|
|
188
187
|
})
|
|
189
188
|
.catch((error) => {
|
|
190
189
|
console.error('An error occurred:', error);
|
|
191
190
|
});
|
|
192
191
|
});
|
|
193
192
|
},
|
|
194
193
|
/**
|
|
195
194
|
* 校验全部数据
|
|
196
195
|
* @param {Object} options
|
|
197
196
|
* @param {Boolean} options.showErrorMsg -是否显示校验信息,
|
|
198
197
|
*/
|
|
199
198
|
validate(options) {
|
|
200
199
|
// 开发环境才提示,生产环境不会提示
|
|
201
200
|
if (process.env.NODE_ENV === 'development' && Object.keys(this.formRules).length === 0) {
|
|
202
201
|
error('未设置rules,请看文档说明!如果已经设置,请刷新页面。');
|
|
203
202
|
return;
|
|
204
203
|
}
|
|
205
204
|
return new Promise((resolve, reject) => {
|
|
206
205
|
// $nextTick是必须的,否则model的变更,可能会延后于validate方法
|
|
207
206
|
this.$nextTick(() => {
|
|
208
207
|
// 获取所有form-item的prop,交给validateField方法进行校验
|
|
209
208
|
const formItemProps = this.children.map((item) => item.prop);
|
|
210
209
|
// console.log(formItemProps)
|
|
211
210
|
this.validateField(
|
|
212
211
|
formItemProps,
|
|
213
212
|
(errors) => {
|
|
214
213
|
if (errors.length) {
|
|
215
214
|
// 如果错误提示方式为toast,则进行提示
|
|
216
215
|
this.errorType === 'toast' && toast(errors[0].message);
|
|
217
216
|
reject(errors);
|
|
218
217
|
} else {
|
|
219
218
|
resolve(true);
|
|
220
219
|
}
|
|
221
220
|
},
|
|
222
221
|
null,
|
|
223
222
|
options
|
|
224
223
|
);
|
|
225
224
|
});
|
|
226
225
|
});
|
|
227
226
|
}
|
|
228
227
|
}
|
|
228
|
+
<template>
|
|
229
|
+
<view class="u-form">
|
|
230
|
+
<slot />
|
|
231
|
+
</view>
|
|
232
|
+
</template>
|
|
233
|
+
<script>
|
|
234
|
+
import { props } from './props.js';
|
|
235
|
+
import { mpMixin } from '../../libs/mixin/mpMixin';
|
|
236
|
+
import { mixin } from '../../libs/mixin/mixin';
|
|
237
|
+
import Schema from '../../libs/util/async-validator';
|
|
238
|
+
import { toast, getProperty, setProperty, deepClone, error } from '../../libs/function/index';
|
|
239
|
+
import test from '../../libs/function/test';
|
|
240
|
+
// 去除警告信息
|
|
241
|
+
Schema.warning = function () {};
|
|
242
|
+
/**
|
|
243
|
+
* Form 表单
|
|
244
|
+
* @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
|
|
245
|
+
* @tutorial https://ijry.github.io/uview-plus/components/form.html
|
|
246
|
+
* @property {Object} model 当前form的需要验证字段的集合
|
|
247
|
+
* @property {Object | Function | Array} rules 验证规则
|
|
248
|
+
* @property {String} errorType 错误的提示方式,见上方说明 ( 默认 message )
|
|
249
|
+
* @property {Boolean} borderBottom 是否显示表单域的下划线边框 ( 默认 true )
|
|
250
|
+
* @property {String} labelPosition 表单域提示文字的位置,left-左侧,top-上方 ( 默认 'left' )
|
|
251
|
+
* @property {String | Number} labelWidth 提示文字的宽度,单位px ( 默认 45 )
|
|
252
|
+
* @property {String} labelAlign lable字体的对齐方式 ( 默认 ‘left' )
|
|
253
|
+
* @property {Object} labelStyle lable的样式,对象形式
|
|
254
|
+
* @example <up-formlabelPosition="left" :model="model1" :rules="rules" ref="form1"></up-form>
|
|
255
|
+
*/
|
|
256
|
+
export default {
|
|
257
|
+
name: 'u-form',
|
|
258
|
+
mixins: [mpMixin, mixin, props],
|
|
259
|
+
provide() {
|
|
260
|
+
return {
|
|
261
|
+
uForm: this
|
|
262
|
+
};
|
|
263
|
+
},
|
|
264
|
+
data() {
|
|
265
|
+
return {
|
|
266
|
+
formRules: {},
|
|
267
|
+
// 规则校验器
|
|
268
|
+
validator: {},
|
|
269
|
+
// 原始的model快照,用于resetFields方法重置表单时使用
|
|
270
|
+
originalModel: null
|
|
271
|
+
};
|
|
272
|
+
},
|
|
273
|
+
watch: {
|
|
274
|
+
// 监听规则的变化
|
|
275
|
+
rules: {
|
|
276
|
+
immediate: true,
|
|
277
|
+
handler(n) {
|
|
278
|
+
this.setRules(n);
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
// 监听属性的变化,通知子组件u-form-item重新获取信息
|
|
282
|
+
propsChange(n) {
|
|
283
|
+
if (this.children?.length) {
|
|
284
|
+
this.children.map((child) => {
|
|
285
|
+
// 判断子组件(u-form-item)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值)
|
|
286
|
+
typeof child.updateParentData == 'function' && child.updateParentData();
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
// 监听model的初始值作为重置表单的快照
|
|
291
|
+
model: {
|
|
292
|
+
immediate: true,
|
|
293
|
+
handler(n) {
|
|
294
|
+
if (!this.originalModel) {
|
|
295
|
+
this.originalModel = deepClone(n);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
computed: {
|
|
301
|
+
propsChange() {
|
|
302
|
+
return [this.errorType, this.borderBottom, this.labelPosition, this.labelWidth, this.labelAlign, this.labelStyle];
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
created() {
|
|
306
|
+
// 存储当前form下的所有u-form-item的实例
|
|
307
|
+
// 不能定义在data中,否则微信小程序会造成循环引用而报错
|
|
308
|
+
this.children = [];
|
|
309
|
+
},
|
|
310
|
+
methods: {
|
|
311
|
+
// 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则
|
|
312
|
+
setRules(rules) {
|
|
313
|
+
// 判断是否有规则
|
|
314
|
+
if (Object.keys(rules).length === 0) return;
|
|
315
|
+
if (process.env.NODE_ENV === 'development' && Object.keys(this.model).length === 0) {
|
|
316
|
+
error('设置rules,model必须设置!如果已经设置,请刷新页面。');
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
this.formRules = rules;
|
|
320
|
+
// 重新将规则赋予Validator
|
|
321
|
+
this.validator = new Schema(rules);
|
|
322
|
+
},
|
|
323
|
+
// 清空所有u-form-item组件的内容,本质上是调用了u-form-item组件中的resetField()方法
|
|
324
|
+
resetFields() {
|
|
325
|
+
this.resetModel();
|
|
326
|
+
},
|
|
327
|
+
// 重置model为初始值的快照
|
|
328
|
+
resetModel(obj) {
|
|
329
|
+
// 历遍所有u-form-item,根据其prop属性,还原model的原始快照
|
|
330
|
+
this.children.map((child) => {
|
|
331
|
+
const prop = child?.prop;
|
|
332
|
+
const value = getProperty(this.originalModel, prop);
|
|
333
|
+
setProperty(this.model, prop, value);
|
|
334
|
+
});
|
|
335
|
+
},
|
|
336
|
+
// 清空校验结果
|
|
337
|
+
clearValidate(props) {
|
|
338
|
+
props = [].concat(props);
|
|
339
|
+
this.children.map((child) => {
|
|
340
|
+
// 如果u-form-item的prop在props数组中,则清除对应的校验结果信息
|
|
341
|
+
if (props[0] === undefined || props.includes(child.prop)) {
|
|
342
|
+
child.message = null;
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
},
|
|
346
|
+
// 对部分表单字段进行校验
|
|
347
|
+
async validateField(value, callback, event = null, options) {
|
|
348
|
+
// $nextTick是必须的,否则model的变更,可能会延后于此方法的执行
|
|
349
|
+
this.$nextTick(() => {
|
|
350
|
+
// 校验错误信息,返回给回调方法,用于存放所有form-item的错误信息
|
|
351
|
+
const errorsRes = [];
|
|
352
|
+
// 如果为字符串,转为数组
|
|
353
|
+
value = [].concat(value);
|
|
354
|
+
// 历遍children所有子form-item
|
|
355
|
+
let promises = this.children.map((child) => {
|
|
356
|
+
return new Promise((resolve, reject) => {
|
|
357
|
+
// 用于存放form-item的错误信息
|
|
358
|
+
const childErrors = [];
|
|
359
|
+
if (value.includes(child.prop)) {
|
|
360
|
+
// 获取对应的属性,通过类似'a.b.c'的形式
|
|
361
|
+
const propertyVal = getProperty(this.model, child.prop);
|
|
362
|
+
// 属性链数组
|
|
363
|
+
const propertyChain = child.prop.split('.');
|
|
364
|
+
const propertyName = propertyChain[propertyChain.length - 1];
|
|
365
|
+
let rule = [];
|
|
366
|
+
if (child.itemRules && child.itemRules.length > 0) {
|
|
367
|
+
rule = child.itemRules;
|
|
368
|
+
} else {
|
|
369
|
+
rule = this.formRules[child.prop];
|
|
370
|
+
}
|
|
371
|
+
// 如果不存在对应的规则,直接返回,否则校验器会报错
|
|
372
|
+
if (!rule) {
|
|
373
|
+
resolve();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
// rule规则可为数组形式,也可为对象形式,此处拼接成为数组
|
|
377
|
+
const rules = [].concat(rule);
|
|
378
|
+
// 对rules数组进行校验
|
|
379
|
+
if (!rules.length) {
|
|
380
|
+
resolve();
|
|
381
|
+
}
|
|
382
|
+
for (let i = 0; i < rules.length; i++) {
|
|
383
|
+
const ruleItem = rules[i];
|
|
384
|
+
// 将u-form-item的触发器转为数组形式
|
|
385
|
+
const trigger = [].concat(ruleItem?.trigger);
|
|
386
|
+
// 如果是有传入触发事件,但是此form-item却没有配置此触发器的话,不执行校验操作
|
|
387
|
+
if (event && !trigger.includes(event)) {
|
|
388
|
+
resolve();
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
// 实例化校验对象,传入构造规则
|
|
392
|
+
const validator = new Schema({
|
|
393
|
+
[propertyName]: ruleItem
|
|
394
|
+
});
|
|
395
|
+
validator.validate(
|
|
396
|
+
{
|
|
397
|
+
[propertyName]: propertyVal
|
|
398
|
+
},
|
|
399
|
+
(errors, fields) => {
|
|
400
|
+
if (test.array(errors)) {
|
|
401
|
+
errors.forEach((element) => {
|
|
402
|
+
element.prop = child.prop;
|
|
403
|
+
});
|
|
404
|
+
errorsRes.push(...errors);
|
|
405
|
+
childErrors.push(...errors);
|
|
406
|
+
}
|
|
407
|
+
//没有配置,或者配置了showErrorMsg为true时候,才修改子组件message,默认没有配置
|
|
408
|
+
if (!options || options?.showErrorMsg == true) {
|
|
409
|
+
child.message = childErrors[0]?.message ? childErrors[0].message : null;
|
|
410
|
+
}
|
|
411
|
+
if (i == rules.length - 1) {
|
|
412
|
+
resolve(errorsRes);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
resolve({});
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
// 使用Promise.all来等待所有Promise完成
|
|
423
|
+
Promise.all(promises)
|
|
424
|
+
.then((results) => {
|
|
425
|
+
// 执行回调函数
|
|
426
|
+
typeof callback === 'function' && callback(errorsRes);
|
|
427
|
+
})
|
|
428
|
+
.catch((error) => {
|
|
429
|
+
console.error('An error occurred:', error);
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
},
|
|
433
|
+
/**
|
|
434
|
+
* 校验全部数据
|
|
435
|
+
* @param {Object} options
|
|
436
|
+
* @param {Boolean} options.showErrorMsg -是否显示校验信息,
|
|
437
|
+
*/
|
|
438
|
+
validate(options) {
|
|
439
|
+
// 开发环境才提示,生产环境不会提示
|
|
440
|
+
if (process.env.NODE_ENV === 'development' && Object.keys(this.formRules).length === 0) {
|
|
441
|
+
error('未设置rules,请看文档说明!如果已经设置,请刷新页面。');
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
return new Promise((resolve, reject) => {
|
|
445
|
+
// $nextTick是必须的,否则model的变更,可能会延后于validate方法
|
|
446
|
+
this.$nextTick(() => {
|
|
447
|
+
// 获取所有form-item的prop,交给validateField方法进行校验
|
|
448
|
+
const formItemProps = this.children.map((item) => item.prop);
|
|
449
|
+
// console.log(formItemProps)
|
|
450
|
+
this.validateField(
|
|
451
|
+
formItemProps,
|
|
452
|
+
(errors) => {
|
|
453
|
+
if (errors.length) {
|
|
454
|
+
// 如果错误提示方式为toast,则进行提示
|
|
455
|
+
this.errorType === 'toast' && toast(errors[0].message);
|
|
456
|
+
reject(errors);
|
|
457
|
+
} else {
|
|
458
|
+
resolve(true);
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
null,
|
|
462
|
+
options
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
</script>
|
|
470
|
+
<style scoped></style>
|
|
@@ -1,3 +1,275 @@
|
|
|
1
|
-
<template>
|
|
2
1
|
<view class="u-qrcode" @longpress="longpress">
|
|
3
2
|
<view class="u-qrcode__content" @click="preview">
|
|
4
3
|
<!-- #ifndef APP-NVUE -->
|
|
5
4
|
<canvas class="u-qrcode__canvas" :id="cid" :canvas-id="cid" :style="{ width: size + unit, height: size + unit }" />
|
|
6
5
|
<!-- #endif -->
|
|
7
6
|
<!-- #ifdef APP-NVUE -->
|
|
8
7
|
<gcanvas class="u-qrcode__canvas" ref="gcanvess" :style="{ width: size + unit, height: size + unit }"></gcanvas>
|
|
9
8
|
<!-- #endif -->
|
|
10
9
|
<view v-if="showLoading && loading" class="u-qrcode__loading" :style="{ width: size + unit, height: size + unit }">
|
|
11
10
|
<up-loading-icon vertical :text="loadingText" textSize="14px"></up-loading-icon>
|
|
12
11
|
</view>
|
|
13
12
|
<!-- <image v-show="show" :src="result" :style="{ width: size + unit, height: size + unit }" /> -->
|
|
14
13
|
</view>
|
|
15
14
|
<!-- <up-action-sheet :actions="list" cancelText="取消"
|
|
16
15
|
</view>
|
|
17
16
|
name: 'u-qrcode',
|
|
18
17
|
props: {
|
|
19
18
|
cid: {
|
|
20
19
|
type: String,
|
|
21
20
|
default: 'u-qrcode-canvas' + Math.random().toString()
|
|
22
21
|
},
|
|
23
22
|
size: {
|
|
24
23
|
type: Number,
|
|
25
24
|
default: 200
|
|
26
25
|
},
|
|
27
26
|
unit: {
|
|
28
27
|
type: String,
|
|
29
28
|
default: 'px'
|
|
30
29
|
},
|
|
31
30
|
show: {
|
|
32
31
|
type: Boolean,
|
|
33
32
|
default: true
|
|
34
33
|
},
|
|
35
34
|
val: {
|
|
36
35
|
type: String,
|
|
37
36
|
default: ''
|
|
38
37
|
},
|
|
39
38
|
background: {
|
|
40
39
|
type: String,
|
|
41
40
|
default: '#ffffff'
|
|
42
41
|
},
|
|
43
42
|
foreground: {
|
|
44
43
|
type: String,
|
|
45
44
|
default: '#000000'
|
|
46
45
|
},
|
|
47
46
|
pdground: {
|
|
48
47
|
type: String,
|
|
49
48
|
default: '#000000'
|
|
50
49
|
},
|
|
51
50
|
icon: {
|
|
52
51
|
type: String,
|
|
53
52
|
default: ''
|
|
54
53
|
},
|
|
55
54
|
iconSize: {
|
|
56
55
|
type: Number,
|
|
57
56
|
default: 40
|
|
58
57
|
},
|
|
59
58
|
lv: {
|
|
60
59
|
type: Number,
|
|
61
60
|
default: 3
|
|
62
61
|
},
|
|
63
62
|
onval: {
|
|
64
63
|
type: Boolean,
|
|
65
64
|
default: true
|
|
66
65
|
},
|
|
67
66
|
loadMake: {
|
|
68
67
|
type: Boolean,
|
|
69
68
|
default: true
|
|
70
69
|
},
|
|
71
70
|
usingComponents: {
|
|
72
71
|
type: Boolean,
|
|
73
72
|
default: true
|
|
74
73
|
},
|
|
75
74
|
showLoading: {
|
|
76
75
|
type: Boolean,
|
|
77
76
|
default: true
|
|
78
77
|
},
|
|
79
78
|
loadingText: {
|
|
80
79
|
type: String,
|
|
81
80
|
default: '生成中'
|
|
82
81
|
},
|
|
83
82
|
allowPreview: {
|
|
84
83
|
type: Boolean,
|
|
85
84
|
default: false
|
|
86
85
|
}
|
|
87
86
|
},
|
|
88
87
|
emits: ['result', 'longpress'],
|
|
89
88
|
data() {
|
|
90
89
|
return {
|
|
91
90
|
loading: false,
|
|
92
91
|
result: '',
|
|
93
92
|
popupShow: false,
|
|
94
93
|
list: [
|
|
95
94
|
{
|
|
96
95
|
name: '保存二维码'
|
|
97
96
|
}
|
|
98
97
|
],
|
|
99
98
|
ganvas: null,
|
|
100
99
|
context: '',
|
|
101
100
|
canvasObj: {}
|
|
102
101
|
};
|
|
103
102
|
},
|
|
104
103
|
mounted: function () {
|
|
105
104
|
// #ifdef APP-NVUE
|
|
106
105
|
/*获取元素引用*/
|
|
107
106
|
this.ganvas = this.$refs['gcanvess'];
|
|
108
107
|
/*通过元素引用获取canvas对象*/
|
|
109
108
|
this.canvasObj = enable(this.ganvas, {
|
|
110
109
|
bridge: WeexBridge
|
|
111
110
|
});
|
|
112
111
|
/*获取绘图所需的上下文,目前不支持3d*/
|
|
113
112
|
this.context = this.canvasObj.getContext('2d');
|
|
114
113
|
// #endif
|
|
115
114
|
if (this.loadMake) {
|
|
116
115
|
if (!this._empty(this.val)) {
|
|
117
116
|
setTimeout(() => {
|
|
118
117
|
this._makeCode();
|
|
119
118
|
}, 0);
|
|
120
119
|
}
|
|
121
120
|
}
|
|
122
121
|
},
|
|
123
122
|
methods: {
|
|
124
123
|
_makeCode() {
|
|
125
124
|
let that = this;
|
|
126
125
|
if (!this._empty(this.val)) {
|
|
127
126
|
// #ifndef APP-NVUE
|
|
128
127
|
this.loading = true;
|
|
129
128
|
// #endif
|
|
130
129
|
qrcode = new QRCode({
|
|
131
130
|
context: that, // 上下文环境
|
|
132
131
|
canvasId: that.cid, // canvas-id
|
|
133
132
|
nvueContext: that.context,
|
|
134
133
|
usingComponents: that.usingComponents, // 是否是自定义组件
|
|
135
134
|
showLoading: false, // 是否显示loading
|
|
136
135
|
loadingText: that.loadingText, // loading文字
|
|
137
136
|
text: that.val, // 生成内容
|
|
138
137
|
size: that.size, // 二维码大小
|
|
139
138
|
background: that.background, // 背景色
|
|
140
139
|
foreground: that.foreground, // 前景色
|
|
141
140
|
pdground: that.pdground, // 定位角点颜色
|
|
142
141
|
correctLevel: that.lv, // 容错级别
|
|
143
142
|
image: that.icon, // 二维码图标
|
|
144
143
|
imageSize: that.iconSize, // 二维码图标大小
|
|
145
144
|
cbResult: function (res) {
|
|
146
145
|
// 生成二维码的回调
|
|
147
146
|
that._result(res);
|
|
148
147
|
}
|
|
149
148
|
});
|
|
150
149
|
} else {
|
|
151
150
|
uni.showToast({
|
|
152
151
|
title: '二维码内容不能为空',
|
|
153
152
|
icon: 'none',
|
|
154
153
|
duration: 2000
|
|
155
154
|
});
|
|
156
155
|
}
|
|
157
156
|
},
|
|
158
157
|
_clearCode() {
|
|
159
158
|
this._result('');
|
|
160
159
|
qrcode.clear();
|
|
161
160
|
},
|
|
162
161
|
_saveCode() {
|
|
163
162
|
let that = this;
|
|
164
163
|
if (this.result != '') {
|
|
165
164
|
uni.saveImageToPhotosAlbum({
|
|
166
165
|
filePath: that.result,
|
|
167
166
|
success: function () {
|
|
168
167
|
uni.showToast({
|
|
169
168
|
title: '二维码保存成功',
|
|
170
169
|
icon: 'success',
|
|
171
170
|
duration: 2000
|
|
172
171
|
});
|
|
173
172
|
}
|
|
174
173
|
});
|
|
175
174
|
}
|
|
176
175
|
},
|
|
177
176
|
preview(e) {
|
|
178
177
|
// 预览图片
|
|
179
178
|
// console.log(this.result)
|
|
180
179
|
if (this.allowPreview) {
|
|
181
180
|
uni.previewImage({
|
|
182
181
|
urls: [this.result],
|
|
183
182
|
longPressActions: {
|
|
184
183
|
itemList: ['保存二维码图片'],
|
|
185
184
|
success: function (data) {
|
|
186
185
|
// console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
|
|
187
186
|
switch (data.tapIndex) {
|
|
188
187
|
case 0:
|
|
189
188
|
that._saveCode();
|
|
190
189
|
break;
|
|
191
190
|
}
|
|
192
191
|
},
|
|
193
192
|
fail: function (err) {
|
|
194
193
|
console.log(err.errMsg);
|
|
195
194
|
}
|
|
196
195
|
}
|
|
197
196
|
});
|
|
198
197
|
}
|
|
199
198
|
this.$emit(
|
|
200
199
|
'preview',
|
|
201
200
|
{
|
|
202
201
|
url: this.result
|
|
203
202
|
},
|
|
204
203
|
e
|
|
205
204
|
);
|
|
206
205
|
},
|
|
207
206
|
longpress() {
|
|
208
207
|
this.$emit('longpress', this.result);
|
|
209
208
|
},
|
|
210
209
|
selectClick(index) {
|
|
211
210
|
switch (index) {
|
|
212
211
|
case 0:
|
|
213
212
|
alert('保存二维码');
|
|
214
213
|
this._saveCode();
|
|
215
214
|
break;
|
|
216
215
|
}
|
|
217
216
|
},
|
|
218
217
|
_result(res) {
|
|
219
218
|
this.loading = false;
|
|
220
219
|
this.result = res;
|
|
221
220
|
this.$emit('result', res);
|
|
222
221
|
},
|
|
223
222
|
_empty(v) {
|
|
224
223
|
let tp = typeof v,
|
|
225
224
|
rt = false;
|
|
226
225
|
if (tp == 'number' && String(v) == '') {
|
|
227
226
|
rt = true;
|
|
228
227
|
} else if (tp == 'undefined') {
|
|
229
228
|
rt = true;
|
|
230
229
|
} else if (tp == 'object') {
|
|
231
230
|
if (JSON.stringify(v) == '{}' || JSON.stringify(v) == '[]' || v == null) rt = true;
|
|
232
231
|
} else if (tp == 'string') {
|
|
233
232
|
if (v == '' || v == 'undefined' || v == 'null' || v == '{}' || v == '[]') rt = true;
|
|
234
233
|
} else if (tp == 'function') {
|
|
235
234
|
rt = false;
|
|
236
235
|
}
|
|
237
236
|
return rt;
|
|
238
237
|
}
|
|
239
238
|
},
|
|
240
239
|
watch: {
|
|
241
240
|
size: function (n, o) {
|
|
242
241
|
if (n != o && !this._empty(n)) {
|
|
243
242
|
this.cSize = n;
|
|
244
243
|
if (!this._empty(this.val)) {
|
|
245
244
|
setTimeout(() => {
|
|
246
245
|
this._makeCode();
|
|
247
246
|
}, 100);
|
|
248
247
|
}
|
|
249
248
|
}
|
|
250
249
|
},
|
|
251
250
|
val: function (n, o) {
|
|
252
251
|
if (this.onval) {
|
|
253
252
|
if (n != o && !this._empty(n)) {
|
|
254
253
|
setTimeout(() => {
|
|
255
254
|
this._makeCode();
|
|
256
255
|
}, 0);
|
|
257
256
|
}
|
|
258
257
|
}
|
|
259
258
|
}
|
|
260
259
|
},
|
|
261
260
|
computed: {}
|
|
262
|
-
|
|
263
|
-
|
|
261
|
+
<template>
|
|
262
|
+
<view class="u-qrcode" @longpress="longpress">
|
|
263
|
+
<view class="u-qrcode__content" @click="preview">
|
|
264
|
+
<!-- #ifndef APP-NVUE -->
|
|
265
|
+
<canvas class="u-qrcode__canvas" :id="cid" :canvas-id="cid" :style="{ width: size + unit, height: size + unit }" />
|
|
266
|
+
<!-- #endif -->
|
|
267
|
+
<!-- #ifdef APP-NVUE -->
|
|
268
|
+
<gcanvas class="u-qrcode__canvas" ref="gcanvess" :style="{ width: size + unit, height: size + unit }"></gcanvas>
|
|
269
|
+
<!-- #endif -->
|
|
270
|
+
<view v-if="showLoading && loading" class="u-qrcode__loading" :style="{ width: size + unit, height: size + unit }">
|
|
271
|
+
<up-loading-icon vertical :text="loadingText" textSize="14px"></up-loading-icon>
|
|
272
|
+
</view>
|
|
273
|
+
<!-- <image v-show="show" :src="result" :style="{ width: size + unit, height: size + unit }" /> -->
|
|
274
|
+
</view>
|
|
275
|
+
<!-- <up-action-sheet :actions="list" cancelText="取消" v-model:show="popupShow" @select="selectClick"></up-action-sheet> -->
|
|
276
|
+
</view>
|
|
277
|
+
</template>
|
|
278
|
+
<script>
|
|
279
|
+
import QRCode from './qrcode.js';
|
|
280
|
+
// #ifdef APP-NVUE
|
|
281
|
+
// https://github.com/dcloudio/NvueCanvasDemo/blob/master/README.md
|
|
282
|
+
import { enable, WeexBridge } from '../../libs/util/gcanvas/index.js';
|
|
283
|
+
// #endif
|
|
284
|
+
let qrcode;
|
|
285
|
+
export default {
|
|
286
|
+
name: 'u-qrcode',
|
|
287
|
+
props: {
|
|
288
|
+
cid: {
|
|
289
|
+
type: String,
|
|
290
|
+
default: 'u-qrcode-canvas' + Math.random().toString()
|
|
291
|
+
},
|
|
292
|
+
size: {
|
|
293
|
+
type: Number,
|
|
294
|
+
default: 200
|
|
295
|
+
},
|
|
296
|
+
unit: {
|
|
297
|
+
type: String,
|
|
298
|
+
default: 'px'
|
|
299
|
+
},
|
|
300
|
+
show: {
|
|
301
|
+
type: Boolean,
|
|
302
|
+
default: true
|
|
303
|
+
},
|
|
304
|
+
val: {
|
|
305
|
+
type: String,
|
|
306
|
+
default: ''
|
|
307
|
+
},
|
|
308
|
+
background: {
|
|
309
|
+
type: String,
|
|
310
|
+
default: '#ffffff'
|
|
311
|
+
},
|
|
312
|
+
foreground: {
|
|
313
|
+
type: String,
|
|
314
|
+
default: '#000000'
|
|
315
|
+
},
|
|
316
|
+
pdground: {
|
|
317
|
+
type: String,
|
|
318
|
+
default: '#000000'
|
|
319
|
+
},
|
|
320
|
+
icon: {
|
|
321
|
+
type: String,
|
|
322
|
+
default: ''
|
|
323
|
+
},
|
|
324
|
+
iconSize: {
|
|
325
|
+
type: Number,
|
|
326
|
+
default: 40
|
|
327
|
+
},
|
|
328
|
+
lv: {
|
|
329
|
+
type: Number,
|
|
330
|
+
default: 3
|
|
331
|
+
},
|
|
332
|
+
onval: {
|
|
333
|
+
type: Boolean,
|
|
334
|
+
default: true
|
|
335
|
+
},
|
|
336
|
+
loadMake: {
|
|
337
|
+
type: Boolean,
|
|
338
|
+
default: true
|
|
339
|
+
},
|
|
340
|
+
usingComponents: {
|
|
341
|
+
type: Boolean,
|
|
342
|
+
default: true
|
|
343
|
+
},
|
|
344
|
+
showLoading: {
|
|
345
|
+
type: Boolean,
|
|
346
|
+
default: true
|
|
347
|
+
},
|
|
348
|
+
loadingText: {
|
|
349
|
+
type: String,
|
|
350
|
+
default: '生成中'
|
|
351
|
+
},
|
|
352
|
+
allowPreview: {
|
|
353
|
+
type: Boolean,
|
|
354
|
+
default: false
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
emits: ['result', 'longpress'],
|
|
358
|
+
data() {
|
|
359
|
+
return {
|
|
360
|
+
loading: false,
|
|
361
|
+
result: '',
|
|
362
|
+
popupShow: false,
|
|
363
|
+
list: [
|
|
364
|
+
{
|
|
365
|
+
name: '保存二维码'
|
|
366
|
+
}
|
|
367
|
+
],
|
|
368
|
+
ganvas: null,
|
|
369
|
+
context: '',
|
|
370
|
+
canvasObj: {}
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
mounted: function () {
|
|
374
|
+
// #ifdef APP-NVUE
|
|
375
|
+
/*获取元素引用*/
|
|
376
|
+
this.ganvas = this.$refs['gcanvess'];
|
|
377
|
+
/*通过元素引用获取canvas对象*/
|
|
378
|
+
this.canvasObj = enable(this.ganvas, {
|
|
379
|
+
bridge: WeexBridge
|
|
380
|
+
});
|
|
381
|
+
/*获取绘图所需的上下文,目前不支持3d*/
|
|
382
|
+
this.context = this.canvasObj.getContext('2d');
|
|
383
|
+
// #endif
|
|
384
|
+
if (this.loadMake) {
|
|
385
|
+
if (!this._empty(this.val)) {
|
|
386
|
+
setTimeout(() => {
|
|
387
|
+
this._makeCode();
|
|
388
|
+
}, 0);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
methods: {
|
|
393
|
+
_makeCode() {
|
|
394
|
+
let that = this;
|
|
395
|
+
if (!this._empty(this.val)) {
|
|
396
|
+
// #ifndef APP-NVUE
|
|
397
|
+
this.loading = true;
|
|
398
|
+
// #endif
|
|
399
|
+
qrcode = new QRCode({
|
|
400
|
+
context: that, // 上下文环境
|
|
401
|
+
canvasId: that.cid, // canvas-id
|
|
402
|
+
nvueContext: that.context,
|
|
403
|
+
usingComponents: that.usingComponents, // 是否是自定义组件
|
|
404
|
+
showLoading: false, // 是否显示loading
|
|
405
|
+
loadingText: that.loadingText, // loading文字
|
|
406
|
+
text: that.val, // 生成内容
|
|
407
|
+
size: that.size, // 二维码大小
|
|
408
|
+
background: that.background, // 背景色
|
|
409
|
+
foreground: that.foreground, // 前景色
|
|
410
|
+
pdground: that.pdground, // 定位角点颜色
|
|
411
|
+
correctLevel: that.lv, // 容错级别
|
|
412
|
+
image: that.icon, // 二维码图标
|
|
413
|
+
imageSize: that.iconSize, // 二维码图标大小
|
|
414
|
+
cbResult: function (res) {
|
|
415
|
+
// 生成二维码的回调
|
|
416
|
+
that._result(res);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
} else {
|
|
420
|
+
uni.showToast({
|
|
421
|
+
title: '二维码内容不能为空',
|
|
422
|
+
icon: 'none',
|
|
423
|
+
duration: 2000
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
_clearCode() {
|
|
428
|
+
this._result('');
|
|
429
|
+
qrcode.clear();
|
|
430
|
+
},
|
|
431
|
+
_saveCode() {
|
|
432
|
+
let that = this;
|
|
433
|
+
if (this.result != '') {
|
|
434
|
+
uni.saveImageToPhotosAlbum({
|
|
435
|
+
filePath: that.result,
|
|
436
|
+
success: function () {
|
|
437
|
+
uni.showToast({
|
|
438
|
+
title: '二维码保存成功',
|
|
439
|
+
icon: 'success',
|
|
440
|
+
duration: 2000
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
preview(e) {
|
|
447
|
+
// 预览图片
|
|
448
|
+
// console.log(this.result)
|
|
449
|
+
if (this.allowPreview) {
|
|
450
|
+
uni.previewImage({
|
|
451
|
+
urls: [this.result],
|
|
452
|
+
longPressActions: {
|
|
453
|
+
itemList: ['保存二维码图片'],
|
|
454
|
+
success: function (data) {
|
|
455
|
+
// console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
|
|
456
|
+
switch (data.tapIndex) {
|
|
457
|
+
case 0:
|
|
458
|
+
that._saveCode();
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
fail: function (err) {
|
|
463
|
+
console.log(err.errMsg);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
this.$emit(
|
|
469
|
+
'preview',
|
|
470
|
+
{
|
|
471
|
+
url: this.result
|
|
472
|
+
},
|
|
473
|
+
e
|
|
474
|
+
);
|
|
475
|
+
},
|
|
476
|
+
longpress() {
|
|
477
|
+
this.$emit('longpress', this.result);
|
|
478
|
+
},
|
|
479
|
+
selectClick(index) {
|
|
480
|
+
switch (index) {
|
|
481
|
+
case 0:
|
|
482
|
+
alert('保存二维码');
|
|
483
|
+
this._saveCode();
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
_result(res) {
|
|
488
|
+
this.loading = false;
|
|
489
|
+
this.result = res;
|
|
490
|
+
this.$emit('result', res);
|
|
491
|
+
},
|
|
492
|
+
_empty(v) {
|
|
493
|
+
let tp = typeof v,
|
|
494
|
+
rt = false;
|
|
495
|
+
if (tp == 'number' && String(v) == '') {
|
|
496
|
+
rt = true;
|
|
497
|
+
} else if (tp == 'undefined') {
|
|
498
|
+
rt = true;
|
|
499
|
+
} else if (tp == 'object') {
|
|
500
|
+
if (JSON.stringify(v) == '{}' || JSON.stringify(v) == '[]' || v == null) rt = true;
|
|
501
|
+
} else if (tp == 'string') {
|
|
502
|
+
if (v == '' || v == 'undefined' || v == 'null' || v == '{}' || v == '[]') rt = true;
|
|
503
|
+
} else if (tp == 'function') {
|
|
504
|
+
rt = false;
|
|
505
|
+
}
|
|
506
|
+
return rt;
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
watch: {
|
|
510
|
+
size: function (n, o) {
|
|
511
|
+
if (n != o && !this._empty(n)) {
|
|
512
|
+
this.cSize = n;
|
|
513
|
+
if (!this._empty(this.val)) {
|
|
514
|
+
setTimeout(() => {
|
|
515
|
+
this._makeCode();
|
|
516
|
+
}, 100);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
val: function (n, o) {
|
|
521
|
+
if (this.onval) {
|
|
522
|
+
if (n != o && !this._empty(n)) {
|
|
523
|
+
setTimeout(() => {
|
|
524
|
+
this._makeCode();
|
|
525
|
+
}, 0);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
computed: {}
|
|
531
|
+
};
|
|
532
|
+
</script>
|
|
533
|
+
<style scoped>
|
|
534
|
+
@import './style.css';
|
|
535
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @action 检测图片协议,主要用于检测海报图片协议。
|
|
3
|
+
* @param {String} imgPath 图片地址。
|
|
4
|
+
*/
|
|
5
|
+
export function checkImgHttp(imgPath) {
|
|
6
|
+
let newPath = '';
|
|
7
|
+
let pathArr = imgPath.split('://');
|
|
8
|
+
// #ifdef H5
|
|
9
|
+
let ishttps = 'https:' == window.location.protocol ? true : false;
|
|
10
|
+
ishttps ? (pathArr[0] = 'https') : (pathArr[0] = 'http');
|
|
11
|
+
// #endif
|
|
12
|
+
// #ifdef MP-WEIXIN
|
|
13
|
+
pathArr[0] = 'https';
|
|
14
|
+
// #endif
|
|
15
|
+
newPath = pathArr.join('://');
|
|
16
|
+
return newPath;
|
|
17
|
+
}
|
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
import { toast } from '../function/index.js';
|
|
2
|
+
import { colorToRgba } from '../function/colorGradient.js';
|
|
3
|
+
export function cc_toast(title, duration = 1600) {
|
|
4
|
+
toast(title, duration);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* 判断是否为空
|
|
8
|
+
*/
|
|
9
|
+
export function isAllEmpty(value) {
|
|
10
|
+
switch (typeof value) {
|
|
11
|
+
case 'undefined':
|
|
12
|
+
return true;
|
|
13
|
+
case 'string':
|
|
14
|
+
if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true;
|
|
15
|
+
break;
|
|
16
|
+
case 'boolean':
|
|
17
|
+
if (!value) return true;
|
|
18
|
+
break;
|
|
19
|
+
case 'number':
|
|
20
|
+
if (value === 0 || isNaN(value)) return true;
|
|
21
|
+
break;
|
|
22
|
+
case 'object':
|
|
23
|
+
if (value === null || value.length === 0) return true;
|
|
24
|
+
for (const i in value) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
1
32
|
async function copyToClipboard(text, callSucc, callError) {
|
|
2
33
|
try {
|
|
3
34
|
await navigator.clipboard.writeText(text);
|
|
@@ -31,9 +62,9 @@ export function cc_call_phone(phone) {
|
|
|
31
62
|
export function cc_copy(data) {
|
|
32
63
|
if (typeof data == 'string') {
|
|
33
64
|
uni.setClipboardData({
|
|
34
|
-
data:
|
|
35
|
-
success:
|
|
36
|
-
|
|
65
|
+
data: data,
|
|
66
|
+
success: () => {
|
|
67
|
+
cc_toast('复制成功');
|
|
37
68
|
}
|
|
38
69
|
});
|
|
39
70
|
} else if (typeof data == 'object') {
|
|
@@ -88,7 +119,6 @@ export function cc_rgbaToRgb(rgba) {
|
|
|
88
119
|
return rgba; // 如果输入的不是合法的RGBA格式,直接返回原值
|
|
89
120
|
}
|
|
90
121
|
}
|
|
91
|
-
import { colorToRgba } from '../function/colorGradient.js';
|
|
92
122
|
export function cc_colorParseRgba(color, alpha) {
|
|
93
123
|
let color_rba = cc_rgbaToRgb(color);
|
|
94
124
|
return colorToRgba(color_rba, alpha);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @action 坐标转换,腾讯地图转换成百度地图坐标
|
|
3
|
+
* @param {String} lng 腾讯经度(pointy)
|
|
4
|
+
* @param {String} lat 腾讯纬度(pointx)
|
|
5
|
+
* @return {Array} 经度,纬度
|
|
6
|
+
*/
|
|
7
|
+
export function qqMapToBdMap(lng, lat) {
|
|
8
|
+
if (lng == null || lng == '' || lat == null || lat == '') return [lng, lat];
|
|
9
|
+
|
|
10
|
+
var x_pi = 3.14159265358979324;
|
|
11
|
+
var x = parseFloat(lng);
|
|
12
|
+
var y = parseFloat(lat);
|
|
13
|
+
var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
|
|
14
|
+
var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
|
|
15
|
+
var lng = (z * Math.cos(theta) + 0.0065).toFixed(5);
|
|
16
|
+
var lat = (z * Math.sin(theta) + 0.006).toFixed(5);
|
|
17
|
+
return [lng, lat];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @action 坐标转换,百度地图坐标转换成腾讯地图坐标
|
|
22
|
+
* @param {String} lng 腾讯经度(pointy)
|
|
23
|
+
* @param {String} lat 腾讯纬度(pointx)
|
|
24
|
+
* @return {Array} 经度,纬度
|
|
25
|
+
*/
|
|
26
|
+
export function bdMapToQQMap(lng, lat) {
|
|
27
|
+
if (lng == null || lng == '' || lat == null || lat == '') return [lng, lat];
|
|
28
|
+
|
|
29
|
+
var x_pi = 3.14159265358979324;
|
|
30
|
+
var x = parseFloat(lng) - 0.0065;
|
|
31
|
+
var y = parseFloat(lat) - 0.006;
|
|
32
|
+
var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
|
|
33
|
+
var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
|
|
34
|
+
var lng = (z * Math.cos(theta)).toFixed(7);
|
|
35
|
+
var lat = (z * Math.sin(theta)).toFixed(7);
|
|
36
|
+
|
|
37
|
+
return [lng, lat];
|
|
38
|
+
}
|
|
@@ -17,15 +17,19 @@ export function deepClone(obj, cache = new WeakMap()) {
|
|
|
17
17
|
clone = new RegExp(obj);
|
|
18
18
|
} else if (obj instanceof Map) {
|
|
19
19
|
clone = new Map(Array.from(obj, ([key, value]) => [key, deepClone(value, cache)]));
|
|
20
|
+
// clone = new Map(Array.from(obj, ([key, value]) => [key, deepMerge(value, cache)]));
|
|
20
21
|
} else if (obj instanceof Set) {
|
|
21
22
|
clone = new Set(Array.from(obj, (value) => deepClone(value, cache)));
|
|
23
|
+
// clone = new Set(Array.from(obj, (value) => deepMerge(value, cache)));
|
|
22
24
|
} else if (Array.isArray(obj)) {
|
|
23
25
|
clone = obj.map((value) => deepClone(value, cache));
|
|
26
|
+
//clone = obj.map((value) => deepMerge(value, cache));
|
|
24
27
|
} else if (Object.prototype.toString.call(obj) === '[object Object]') {
|
|
25
28
|
clone = Object.create(Object.getPrototypeOf(obj));
|
|
26
29
|
cache.set(obj, clone);
|
|
27
30
|
for (const [key, value] of Object.entries(obj)) {
|
|
28
31
|
clone[key] = deepClone(value, cache);
|
|
32
|
+
//clone[key] = deepMerge(value, cache);
|
|
29
33
|
}
|
|
30
34
|
} else {
|
|
31
35
|
clone = Object.assign({}, obj);
|
|
@@ -42,6 +46,7 @@ export function deepClone(obj, cache = new WeakMap()) {
|
|
|
42
46
|
*/
|
|
43
47
|
function deepMerge(target = {}, source = {}) {
|
|
44
48
|
target = deepClone(target);
|
|
49
|
+
// target = deepMerge(target);
|
|
45
50
|
if (typeof target !== 'object' || target === null || typeof source !== 'object' || source === null) return target;
|
|
46
51
|
const merged = Array.isArray(target) ? target.slice() : Object.assign({}, target);
|
|
47
52
|
for (const prop in source) {
|