@cloudbase/lowcode-builder 1.10.24 → 1.10.28

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.
@@ -1,552 +1,706 @@
1
1
  import {
2
- app,
3
- $app
2
+ app,
3
+ $app
4
4
  } from '../../../../app/weapps-api';
5
5
  import loginByPassword from './methods/loginByPassword';
6
6
  import loginByWXPhone from './methods/loginByWXPhone';
7
7
  import {
8
- parseSmsError
8
+ parseSmsError,
9
+ parseEmailError
9
10
  } from './methods/errorHandler';
10
11
  import loginBySms from './methods/loginBySms';
12
+ import loginByEmail from './methods/loginByEmail';
11
13
  import loginByOpenId from './methods/loginByOpenId';
12
14
  import loginByUnionId from './methods/loginByUnionId';
13
15
  import {
14
- getLoginConfig
16
+ getLoginConfig
15
17
  } from '../../../../common/util';
16
18
  import {
17
- phoneNumberPattern,
18
- phoneVerifyInfoKey,
19
- verifyDelay,
20
- loginOnly
19
+ phoneNumberPattern,
20
+ phoneVerifyInfoKey,
21
+ emailVerifyInfoKey,
22
+ emailPattern,
23
+ verifyDelay,
21
24
  } from './methods/contants';
22
25
  import loginSuccessCallBack from './methods/loginSuccessCallBack'
26
+ import {
27
+ calDelay,
28
+ compareVersion,
29
+ randomStr
30
+ } from './methods/utils';
23
31
 
24
- const AUTO_LOGIN_TYPE = ['openid_login', 'unionid_login']
32
+ const AUTO_LOGIN_TYPE = ['__openid_login', '__unionid_login']
25
33
  const AUTO_LOGIN_METHOD = {
26
- 'openid_login': loginByOpenId,
27
- 'unionid_login': loginByUnionId
34
+ '__openid_login': loginByOpenId,
35
+ '__unionid_login': loginByUnionId
28
36
  }
29
37
 
30
38
  function decodePageQuery(query) {
31
- return Object.keys(query).reduce((decoded, key) => {
32
- decoded[key] = decodeURIComponent(query[key]);
33
- return decoded;
34
- }, {});
39
+ return Object.keys(query).reduce((decoded, key) => {
40
+ decoded[key] = decodeURIComponent(query[key]);
41
+ return decoded;
42
+ }, {});
35
43
  }
36
44
 
37
45
  const BUTTON_CLASSNAME = 'weda-ui weda-button weui-btn weui-btn_primary wd-event-tap';
38
46
 
39
- function setStorage(data) {
40
- const current = wx.getStorageSync(phoneVerifyInfoKey);
41
- wx.setStorageSync(phoneVerifyInfoKey, {
42
- ...current,
43
- ...data
44
- });
47
+ function setStorage(data, key = phoneVerifyInfoKey) {
48
+ const current = wx.getStorageSync(key || phoneVerifyInfoKey);
49
+ wx.setStorageSync(key, {
50
+ ...current,
51
+ ...data
52
+ });
45
53
  }
46
54
 
47
55
  Component({
48
- data: {
49
- params: {},
50
- initing: true,
51
- error: null,
52
- settingData: {
53
- logo: '',
54
- agreement: {
55
- items: [],
56
- enable: false,
57
- },
58
- miniprogram: [],
59
- },
60
- pageStyle: '',
61
- agreement: false,
62
- wxLoginStatus: '',
63
- passwordVisible: false,
64
- enablePassword: false,
65
- enableMpPhone: false,
66
- enbaleSms: false,
67
- disableLoginSubmit: false,
68
- loginButtonClass: BUTTON_CLASSNAME,
69
- phoneValidateMessage: '手机号为空',
70
- phoneNum: '',
71
- defaultPhoneNumber: '',
72
- smsVerificationInfo: {
73
- id: '',
74
- isUser: false,
75
- },
76
-
77
- currentLoginType: '',
78
- currentLoginList: [],
79
- baseInfoShow: false,
80
- avatarUrl: '',
81
- avatarRealUrl: '',
82
- nickName: '',
83
- publicPage: []
84
- },
85
- methods: {
86
- async getPhoneCodeNumber(e) {
87
- const agree = this.checkAgreement();
88
- if (!agree) {
89
- return;
90
- }
91
- if (e?.detail?.errMsg?.includes('fail') || e?.detail?.errno || !e?.detail?.code) {
92
- console.warn('获取手机号授权令牌失败:', !e?.detail.code ? {
93
- errMsg: '可能基础库版本过低'
94
- } : e.detail);
95
-
96
- $app.showModal({
97
- cancelColor: '#000000',
98
- cancelText: '取消',
99
- confirmColor: '#576B95',
100
- confirmText: '确认',
101
- showCancel: true,
102
- title: '获取手机号授权令牌失败',
103
- content: `获取手机号授权令牌失败: ${e.detail.errno || e.detail?.errMsg || '可能基础库版本过低'}`,
104
- });
105
-
106
- return;
107
- }
108
-
109
- return loginByWXPhone(this, {
110
- event: e
111
- });
112
- },
113
- async onLoad(options) {
114
- try {
115
- const cache = wx.getStorageSync(phoneVerifyInfoKey);
116
- if (cache.phoneNum) {
117
- this.setData({
118
- defaultPhoneNumber: cache.phoneNum,
119
- phoneNum: cache.phoneNum,
120
- phoneValidateMessage: ''
121
- });
122
- }
123
-
124
- const config = await getLoginConfig().catch(() => {
125
- return getLoginConfig();
126
- });
127
- const query = decodePageQuery(options || {});
128
- let settingData = {
129
- logo: app.__internal__.resolveStaticResourceUrl(config.logo) ||
130
- 'https://sso-1303824488.cos.ap-shanghai.myqcloud.com/logo.svg',
56
+ data: {
57
+ params: {},
58
+ initing: true,
59
+ error: null,
60
+ settingData: {
61
+ logo: '',
62
+ agreement: {
63
+ items: [],
64
+ enable: false,
65
+ },
66
+ miniprogram: [],
67
+ },
68
+ pageStyle: '',
69
+ agreement: false,
70
+ wxLoginStatus: '',
71
+ passwordVisible: false,
72
+ enablePassword: false,
73
+ enableMpPhone: false,
74
+ enableSms: false,
75
+ enableEmail: false,
76
+ disableLoginSubmit: false,
77
+ loginButtonClass: BUTTON_CLASSNAME,
78
+
79
+ phoneValidateMessage: '手机号为空',
80
+ phoneNum: '',
81
+ smsDelay: 0,
82
+ defaultPhoneNumber: '',
83
+ smsVerificationInfo: {
84
+ id: '',
85
+ isUser: false,
86
+ },
87
+
88
+ currentLoginType: '',
89
+ currentLoginList: [],
90
+ baseInfoShow: false,
91
+ avatarUrl: '',
92
+ avatarRealUrl: '',
93
+ nickName: '',
94
+ publicPage: [],
95
+
96
+ emailValidateMessage: '邮箱为空',
97
+ emailDelay: 0,
98
+ email: '',
99
+ defaultEmail: '',
100
+ emailVerificationInfo: {
101
+ id: '',
102
+ isUser: false,
103
+ },
104
+
105
+ isRegister: false, // 是否注册场景
106
+ registerMethods: [], // 注册方式
107
+ },
108
+ methods: {
109
+ async getPhoneCodeNumber(e) {
110
+ const agree = this.checkAgreement();
111
+ if (!agree) {
112
+ return;
113
+ }
114
+ if (e?.detail?.errMsg?.includes('fail') || e?.detail?.errno || !e?.detail?.code) {
115
+ console.warn('获取手机号授权令牌失败:', !e?.detail.code ? {
116
+ errMsg: '可能基础库版本过低'
117
+ } : e.detail);
118
+
119
+ $app.showModal({
120
+ cancelColor: '#000000',
121
+ cancelText: '取消',
122
+ confirmColor: '#576B95',
123
+ confirmText: '确认',
124
+ showCancel: true,
125
+ title: '获取手机号授权令牌失败',
126
+ content: `获取手机号授权令牌失败: ${e.detail.errno || e.detail?.errMsg || '可能基础库版本过低'}`,
127
+ });
128
+
129
+ return;
130
+ }
131
+
132
+ return loginByWXPhone(this, {
133
+ event: e
134
+ });
135
+ },
136
+ getCacheInfo(storageKey, dataKeysMap) {
137
+ const cache = wx.getStorageSync(storageKey);
138
+ const data = {}
139
+
140
+ if (cache[dataKeysMap.judgeKey]) {
141
+ Object.keys(dataKeysMap).forEach(key => {
142
+ if (dataKeysMap[key] === 'cache') {
143
+ data[key] = cache
144
+ } else if (key !== 'judgeKey') {
145
+ data[key] = cache[dataKeysMap[key]] ?? ''
146
+ }
147
+ })
148
+
149
+ this.setData(data);
150
+ }
151
+
152
+ return cache
153
+ },
154
+ dealPhoneAndEmailCache(settingData) {
155
+ const phoneCache = this.getCacheInfo(phoneVerifyInfoKey, {
156
+ judgeKey: 'phoneNum',
157
+ defaultPhoneNumber: 'phoneNum',
158
+ phoneNum: 'phoneNum',
159
+ phoneValidateMessage: 'phoneValidateMessage',
160
+ smsVerificationInfo: 'cache'
161
+ })
162
+ const emailCache = this.getCacheInfo(emailVerifyInfoKey, {
163
+ judgeKey: 'email',
164
+ defaultEmail: 'email',
165
+ email: 'email',
166
+ emailValidateMessage: 'emailValidateMessage',
167
+ emailVerificationInfo: 'cache'
168
+ })
169
+
170
+ const enableSms = this._getEnable(settingData, '__sms');
171
+ const enableEmail = this._getEnable(settingData, 'email');
172
+ const smsDelay = calDelay(enableSms && phoneCache.phoneNum, phoneCache?.smsDelay);
173
+ const emailDelay = calDelay(enableEmail && emailCache.email, emailCache?.emailDelay);
174
+ this._intervalSender(smsDelay, '__sms');
175
+ this._intervalSender(emailDelay, 'email');
176
+ this.setData({
177
+ enableSms,
178
+ smsDelay,
179
+ enableEmail,
180
+ emailDelay,
181
+ })
182
+ },
183
+ async onLoad(options) {
184
+ try {
185
+ const config = await getLoginConfig().catch(() => {
186
+ return getLoginConfig();
187
+ });
188
+ const query = decodePageQuery(options || {});
189
+ let settingData = {
190
+ logo: app.__internal__.resolveStaticResourceUrl(config.logo) ||
191
+ 'https://sso-1303824488.cos.ap-shanghai.myqcloud.com/logo.svg',
131
192
  name: config.name,
132
- agreement: {
133
- items: [],
134
- enable: false,
135
- ...config.agreement,
136
- },
137
- miniprogram: (config.miniprogram || []).map(v => ({
138
- ...v,
139
- label: v.label?.replace(/^微信小程序/, '')
193
+ agreement: {
194
+ items: [],
195
+ enable: false,
196
+ ...config.agreement,
197
+ },
198
+ miniprogram: (config.miniprogram || []).map(v => ({
199
+ ...v,
200
+ label: v.label?.replace(/^微信小程序/, '')
140
201
  })),
141
- access: { ...config.access },
142
- };
143
-
144
- const enableSms = this._getEnableSms(settingData);
145
- const smsDelay =
146
- enableSms && !cache.phoneNum ? Math.max(0, Math.ceil(((cache?.smsDelay || 0) - +new Date()) / 1000)) : 0;
147
- const baseInfoShow = !settingData.access?.disabledCollectUserInfo && query.baseInfoShow === 'true'
148
- const currentLoginType = query.currentLoginType || settingData.miniprogram.filter(v => !AUTO_LOGIN_TYPE.includes(v.type))?.[0]?.type || settingData.miniprogram[0]?.type;
149
- const currentLoginList = query.currentLoginType ? [] : settingData.miniprogram.filter(v => v.type !== currentLoginType)
150
-
151
- const {
152
- avatarUrl,
153
- nickName
154
- } = app.auth.currentUser
155
- const avatarRealUrl = await this.getAvatarUrl(avatarUrl)
156
-
157
- this.setData({
158
- params: query,
159
- settingData,
160
- enablePassword: this._getEnablePassword(settingData),
161
- enableMpPhone: this._getEnableMpPhone(settingData),
162
- enableSms,
163
- smsDelay,
164
- smsVerificationInfo: cache,
165
- pageStyle: `${this.data.pageStyle}; background-color: ${config.backgroundColor || '#fff'};`,
166
- initing: false,
167
- currentLoginType,
168
- currentLoginList,
169
- baseInfoShow,
170
- avatarRealUrl,
171
- avatarUrl,
172
- nickName,
202
+ access: {
203
+ ...config.access
204
+ },
205
+ userRegistry: {
206
+ ...config.userRegistry
207
+ }
208
+ };
209
+
210
+ this.dealPhoneAndEmailCache(settingData)
211
+
212
+ const baseInfoShow = !settingData.access?.disabledCollectUserInfo && query.baseInfoShow === 'true'
213
+ const currentLoginType = query.currentLoginType || settingData.miniprogram.filter(v => !AUTO_LOGIN_TYPE.includes(v.id))?.[0]?.id || settingData.miniprogram[0]?.id;
214
+ const currentLoginList = query.currentLoginType ? [] : settingData.miniprogram
215
+
216
+ const {
217
+ avatarUrl,
218
+ nickName
219
+ } = app.auth.currentUser
220
+ const avatarRealUrl = await this.getAvatarUrl(avatarUrl)
221
+
222
+ this.setData({
223
+ params: query,
224
+ settingData,
225
+ enablePassword: this._getEnable(settingData, 'password'),
226
+ enableMpPhone: this._getEnable(settingData, 'miniprogram_phone'),
227
+ pageStyle: `${this.data.pageStyle}; background-color: ${config.backgroundColor || '#fff'};`,
228
+ initing: false,
229
+ currentLoginType,
230
+ currentLoginList,
231
+ baseInfoShow,
232
+ avatarRealUrl,
233
+ avatarUrl,
234
+ nickName,
173
235
  publicPage: (app.__internal__?.getConfig() || {})?.publicPage || [],
174
- });
175
-
176
- if (smsDelay > 0) {
177
- this._intervalSender(smsDelay);
178
- }
179
-
180
- if (settingData.miniprogram.length === 1 && AUTO_LOGIN_TYPE.includes(currentLoginType) && !baseInfoShow) {
181
- await this.dealOpenIdOrUnionIdLogin(currentLoginType);
182
- }
183
- } catch (e) {
184
- console.error('获取登录配置失败:', e);
185
- this.setData({
186
- initing: false,
187
- error: {
188
- message: '获取登录配置失败,' + (e.message || ''),
189
- },
190
- });
191
- }
192
- },
193
- togglePasswordVisible() {
194
- this.setData({
195
- passwordVisible: !this.data.passwordVisible,
196
- });
197
- },
198
- loginByPassword(e) {
199
- if (!this.checkAgreement()) {
200
- return;
201
- }
202
-
203
- const values = e.detail.value;
204
-
205
- return loginByPassword(this, {
206
- phone: values?.username?.value,
207
- sms: values?.password?.value,
208
- });
209
- },
210
- loginBySms(e) {
211
- if (!this.checkAgreement()) {
212
- return;
213
- }
214
-
215
- const values = e.detail.value;
216
-
217
- return loginBySms(this, {
218
- phoneNum: values?.phoneNum,
219
- verificationCode: values?.verificationCode,
220
- verificationInfo: this.data.smsVerificationInfo,
221
- callback: (err, data) => {
222
- if (err) {
223
- const {
224
- usedCount = 0
225
- } = wx.getStorageSync(phoneVerifyInfoKey) || {};
226
- setStorage({
227
- usedCount: usedCount + 1
228
- });
229
- } else {
230
- setStorage({
231
- usedCount: 0,
232
- smsDelay: 0
233
- });
234
- }
235
- },
236
- });
237
- },
238
- showAgreement(e) {
239
- const index = e.currentTarget?.dataset?.index;
240
- const agreementContent = this.data.settingData.agreement.items[index]?.value;
241
- this.setData({
242
- agreementContent,
243
- isShowAgreement: !!agreementContent,
244
- });
245
- },
246
- checkAgreement() {
247
- if (this.data.settingData?.agreement?.enable && !this.data.agreement) {
248
- app.showToast({
249
- title: '请阅读勾选协议',
250
- icon: 'error',
251
- duartion: 1500,
252
- });
253
-
254
- return false;
255
- }
256
- return true;
257
- },
258
- onAgreementChange(e) {
259
- this.setData({
260
- agreement: e.detail.value.length > 0,
261
- });
262
- },
263
- onPhoneChange(e) {
264
- const {
265
- value
266
- } = e.detail;
267
- if (!phoneNumberPattern.test(value)) {
268
- this.setData({
269
- phoneValidateMessage: '手机号格式非法',
270
- phoneNum: value
271
- });
272
- } else {
273
- this.setData({
274
- phoneValidateMessage: '',
275
- phoneNum: value
276
- });
277
- }
278
- },
279
- getCaptcha(e) {
280
- if (!this.checkAgreement()) {
281
- return;
282
- }
283
-
284
- if (this.data.phoneValidateMessage) {
285
- wx.showToast({
286
- icon: 'error',
287
- title: this.data.phoneValidateMessage,
288
- });
289
- return;
290
- }
291
- if (this.data.smsDelay > 0) {
292
- return;
293
- }
294
- this._intervalSender(verifyDelay);
295
- const target = loginOnly ? 'USER' : 'ANY';
296
- const phoneNum = this.data.phoneNum;
297
- app.cloud
298
- .getCloudInstance()
299
- .then((cloudbase) => {
300
- return cloudbase.authInstance || cloudbase.auth;
301
- })
302
- .then((auth) => {
303
- setStorage({
304
- phoneNum
305
- });
306
- return auth.getVerification({
307
- target,
308
- phone_number: `+86 ${phoneNum}`,
309
- });
310
- })
311
- .then((data) => {
312
- const verifyInfo = {
313
- id: data.verification_id,
314
- isUser: data.is_user,
315
- };
316
- this.setData({
317
- smsVerificationInfo: verifyInfo,
318
- });
319
- setStorage({
320
- ...verifyInfo,
321
- usedCount: 0
322
- });
323
- wx.showToast({
324
- title: '验证码已发送',
325
- });
326
- })
327
- .catch((e) => {
328
- wx.hideLoading()
329
- clearInterval(this.timerInterval);
330
- this.setData({
331
- smsDelay: 0
332
- });
333
- setStorage({
334
- smsDelay: 0
335
- });
336
- wx.showModal({
337
- title: '获取验证码失败',
338
- content: parseSmsError(e, target, loginOnly),
339
- showCancel: false,
340
- });
341
- });
342
- },
343
- _getEnablePassword(settingData) {
344
- const config = settingData || this.data.settingData;
345
- return config.miniprogram?.find?.((item) => item.type === 'password')?.enable ?? false;
346
- },
347
- _getEnableMpPhone(settingData) {
348
- const config = settingData || this.data.settingData;
349
- return !!config.miniprogram.find(function (item) {
350
- return item.type === 'miniprogram_phone';
351
- })?.enable;
352
- },
353
- _getEnableSms(settingData) {
354
- const config = settingData || this.data.settingData;
355
- return !!config.miniprogram.find(function (item) {
356
- return item.type === 'sms';
357
- })?.enable;
358
- },
359
- _intervalSender(delay) {
360
- this.setData({
361
- smsDelay: delay
362
- });
363
- const timerInterval = setInterval(() => {
364
- delay = delay - 1;
365
- if (delay <= 0) {
366
- clearInterval(timerInterval);
367
- }
368
- this.setData({
369
- smsDelay: delay
370
- });
371
- setStorage({
372
- smsDelay: +new Date() + delay * 1000
373
- });
374
- }, 1000);
375
- this.timerInterval = timerInterval;
376
- },
377
- compareVersion(version1, version2) {
378
- // 将版本号字符串分割成数组
379
- const v1 = version1.split('.');
380
- const v2 = version2.split('.');
381
-
382
- // 获取两个版本号数组的最大长度
383
- const maxLength = Math.max(v1.length, v2.length);
384
-
385
- // 遍历版本号数组并比较每个部分
386
- for (let i = 0; i < maxLength; i++) {
387
- // 如果某个版本号的长度较短,则将其补0
388
- const num1 = parseInt(v1[i] || 0, 10);
389
- const num2 = parseInt(v2[i] || 0, 10);
390
-
391
- // 比较当前位置的数字大小
392
- if (num1 > num2) {
393
- return 1;
394
- } else if (num1 < num2) {
395
- return -1;
396
- }
397
- }
398
-
399
- // 如果所有位置的数字都相同,则两个版本号相等
400
- return 0;
401
- },
402
- async getAvatarUrl(avatarUrl) {
403
- let avatarRealUrl = avatarUrl
404
- if (avatarUrl.startsWith('cloud://')) {
405
- const tcb = await app.cloud.getCloudInstance()
406
- const pic = await tcb.getTempFileURL({
407
- fileList: [avatarUrl]
408
- })
409
- avatarRealUrl = pic?.fileList?.[0]?.tempFileURL || ''
410
- }
411
-
412
- return avatarRealUrl;
413
- },
414
- async dealOpenIdOrUnionIdLogin(type) {
415
- try {
416
- const res = await (AUTO_LOGIN_METHOD[type])(this);
417
- if (res) {
418
- const {
419
- avatarUrl,
420
- nickName
421
- } = app.auth.currentUser
422
- const {
423
- SDKVersion
424
- } = wx.getSystemInfoSync();
236
+ registerMethods: settingData.userRegistry.enable && settingData.userRegistry.miniprogram || []
237
+ });
238
+
239
+
240
+ if (settingData.miniprogram.length === 1 && AUTO_LOGIN_TYPE.includes(currentLoginType) && !baseInfoShow) {
241
+ await this.dealOpenIdOrUnionIdLogin(currentLoginType);
242
+ }
243
+ } catch (e) {
244
+ console.error('获取登录配置失败:', e);
245
+ this.setData({
246
+ initing: false,
247
+ error: {
248
+ message: '获取登录配置失败,' + (e.message || ''),
249
+ },
250
+ });
251
+ }
252
+ },
253
+ togglePasswordVisible() {
254
+ this.setData({
255
+ passwordVisible: !this.data.passwordVisible,
256
+ });
257
+ },
258
+ loginByPassword(e) {
259
+ if (!this.checkAgreement()) {
260
+ return;
261
+ }
262
+
263
+ const values = e.detail.value;
264
+
265
+ return loginByPassword(this, {
266
+ username: values?.username,
267
+ password: values?.password,
268
+ });
269
+ },
270
+ showAgreement(e) {
271
+ const index = e.currentTarget?.dataset?.index;
272
+ const agreementContent = this.data.settingData.agreement.items[index]?.value;
273
+ this.setData({
274
+ agreementContent,
275
+ isShowAgreement: !!agreementContent,
276
+ });
277
+ },
278
+ checkAgreement() {
279
+ if (this.data.settingData?.agreement?.enable && !this.data.agreement) {
280
+ app.showToast({
281
+ title: '请阅读勾选协议',
282
+ icon: 'error',
283
+ duartion: 1500,
284
+ });
285
+
286
+ return false;
287
+ }
288
+ return true;
289
+ },
290
+ onAgreementChange(e) {
291
+ this.setData({
292
+ agreement: e.detail.value.length > 0,
293
+ });
294
+ },
295
+ // 通用登录方法
296
+ login(e, {
297
+ verificationInfoKey,
298
+ valueKey,
299
+ storageKey,
300
+ apiFunction
301
+ }) {
302
+ if (!this.checkAgreement()) return;
303
+
304
+ const values = e.detail.value;
305
+ const {
306
+ verificationCode
307
+ } = values;
308
+
309
+ // 动态获取验证信息
310
+ const verificationInfo = this.data[verificationInfoKey];
311
+
312
+ // 执行登录API
313
+ return apiFunction(this, {
314
+ [valueKey]: values[valueKey],
315
+ verificationCode,
316
+ verificationInfo,
317
+ loginOnly: !this.data.settingData.userRegistry?.enable,
318
+ isRegister: this.data.isRegister,
319
+ hasRegisterConfig: this.data.userRegistry?.enable && this.data.userRegistry?.web?.length, // 有注册配置,才会走注册配置的判断
320
+ callback: (err, data) => {
321
+ if (err) {
322
+ // 错误处理:更新尝试次数
323
+ const {
324
+ usedCount = 0
325
+ } = wx.getStorageSync(storageKey) || {};
326
+ setStorage({
327
+ usedCount: usedCount + 1
328
+ }, storageKey)
329
+ } else {
330
+ // 成功处理:重置计数器
331
+ setStorage({
332
+ usedCount: 0,
333
+ smsDelay: 0
334
+ }, storageKey)
335
+ }
336
+ }
337
+ })
338
+ },
339
+ // 点击登录触发
340
+ handleLogin(e) {
341
+ if (this.data.currentLoginType === '__sms') {
342
+ this.login(e, {
343
+ verificationInfoKey: 'smsVerificationInfo',
344
+ storageKey: phoneVerifyInfoKey,
345
+ apiFunction: loginBySms,
346
+ valueKey: 'phoneNum'
347
+ });
348
+ } else if (this.data.currentLoginType === 'email') {
349
+ this.login(e, {
350
+ verificationInfoKey: 'emailVerificationInfo',
351
+ storageKey: emailVerifyInfoKey,
352
+ apiFunction: loginByEmail,
353
+ valueKey: 'email'
354
+ });
355
+ }
356
+ },
357
+ // 通用输入方法
358
+ inputChange(e, {
359
+ validateMsgKey,
360
+ pattern,
361
+ fieldName,
362
+ errorMessage
363
+ }) {
364
+ const {
365
+ value
366
+ } = e.detail;
367
+ const data = {};
368
+
369
+ data[validateMsgKey] = pattern.test(value) ?
370
+ '' : errorMessage;
371
+ data[fieldName] = value;
372
+
373
+ this.setData(data);
374
+ },
375
+ // 用户输入时触发
376
+ handleInputChange(e) {
377
+ if (this.data.currentLoginType === '__sms') {
378
+ this.inputChange(e, {
379
+ validateMsgKey: 'phoneValidateMessage',
380
+ pattern: phoneNumberPattern,
381
+ fieldName: 'phoneNum',
382
+ errorMessage: '手机号格式非法'
383
+ });
384
+ } else if (this.data.currentLoginType === 'email') {
385
+ this.inputChange(e, {
386
+ validateMsgKey: 'emailValidateMessage',
387
+ pattern: emailPattern,
388
+ fieldName: 'email',
389
+ errorMessage: '邮箱格式非法'
390
+ });
391
+ }
392
+ },
393
+ // 通用获取验证码方法
394
+ getCaptcha(options) {
395
+ const {
396
+ // 验证信息校验相关配置
397
+ validateMessageKey, // 校验失败提示信息的data键(如'phoneValidateMessage')
398
+ delayKey, // 延迟计时的data键(如'smsDelay')
399
+
400
+ // 数据标识相关配置
401
+ getInfoField, // 标识字段名(如'phoneNum'或'email')
402
+ targetField, // 接口参数名(如'phone_number'或'email')
403
+ verificationInfoKey, // 存储验证信息的data键(如'smsVerificationInfo')
404
+
405
+ // 存储相关配置
406
+ storageKey, // 标识信息存储键(可选,如'emailVerifyInfoKey')
407
+
408
+ // 错误处理配置
409
+ parseError, // 错误解析函数(如parseSmsError)
410
+ interfacePrefix, // 手机号码前缀(默认'+86 ',邮箱留空)
411
+
412
+ // 定时器配置
413
+ timerIntervalKey // 计时器实例的data键(如'timerInterval')
414
+ } = options;
415
+
416
+ // 1. 检查用户协议
417
+ if (!this.checkAgreement()) return;
418
+
419
+ // 2. 检查已有验证提示信息
420
+ const validateMessage = this.data[validateMessageKey];
421
+ if (validateMessage) {
422
+ wx.showToast({
423
+ icon: 'error',
424
+ title: validateMessage
425
+ });
426
+ return;
427
+ }
428
+
429
+ // 3. 检查冷却时间
430
+ const currentDelay = this.data[delayKey];
431
+ if (currentDelay > 0) return;
432
+
433
+ // 4. 启动发送计时器
434
+ this._intervalSender(verifyDelay);
435
+
436
+ // 5. 确定登录模式和目标类型
437
+ const loginOnly = !this.data.settingData.userRegistry?.enable;
438
+ const target = loginOnly ? 'USER' : 'ANY';
439
+
440
+ // 6. 获取用户输入的标识(手机号/邮箱)
441
+ const identifier = this.data[getInfoField];
442
+
443
+ // 7. 调用云服务获取验证码
444
+ app.cloud.getCloudInstance()
445
+ .then(cloudbase => cloudbase.authInstance || cloudbase.auth)
446
+ .then(auth => {
447
+ // 存储用户标识(手机号/邮箱)
448
+ const baseStorageData = {
449
+ [getInfoField]: identifier
450
+ };
451
+ setStorage(baseStorageData, storageKey)
452
+
453
+ // 构造接口请求参数(手机号需要+86前缀)
454
+ const apiParams = {
455
+ target,
456
+ [targetField]: `${interfacePrefix || ''}${identifier}`
457
+ };
458
+
459
+ return auth.getVerification(apiParams);
460
+ })
461
+ .then(data => {
462
+ // 处理成功响应
463
+ const verifyInfo = {
464
+ id: data.verification_id,
465
+ isUser: data.is_user
466
+ };
467
+
468
+ // 更新页面数据
469
+ this.setData({
470
+ [verificationInfoKey]: verifyInfo
471
+ });
472
+
473
+ // 持久化存储验证信息(含使用次数)
474
+ const persistData = {
475
+ ...verifyInfo,
476
+ usedCount: 0
477
+ };
478
+ setStorage(persistData, storageKey)
479
+
480
+ wx.showToast({
481
+ title: '验证码已发送'
482
+ });
483
+ })
484
+ .catch(e => {
485
+ // 统一处理错误流程
486
+ wx.hideLoading();
487
+ clearInterval(this[timerIntervalKey]); // 停止计时器
488
+ this.setData({
489
+ [delayKey]: 0
490
+ }); // 重置冷却时间
491
+ setStorage({
492
+ [delayKey]: 0
493
+ }, storageKey)
494
+
495
+ // 显示错误提示
496
+ wx.showModal({
497
+ title: '获取验证码失败',
498
+ content: parseError(e, target, loginOnly),
499
+ showCancel: false
500
+ });
501
+ });
502
+ },
503
+ // 用户点击获取验证码触发
504
+ handleCaptcha(e) {
505
+ if (this.data.currentLoginType === '__sms') {
506
+ this.getCaptcha({
507
+ validateMessageKey: 'phoneValidateMessage',
508
+ delayKey: 'smsDelay',
509
+ getInfoField: 'phoneNum',
510
+ targetField: 'phone_number',
511
+ verificationInfoKey: 'smsVerificationInfo',
512
+ storageKey: phoneVerifyInfoKey,
513
+ parseError: (e, t, l) => parseSmsError(e, t, l),
514
+ interfacePrefix: '+86 ',
515
+ timerIntervalKey: 'timerInterval'
516
+ });
517
+ } else if (this.data.currentLoginType === 'email') {
518
+ this.getCaptcha({
519
+ validateMessageKey: 'emailValidateMessage',
520
+ delayKey: 'emailDelay',
521
+ getInfoField: 'email',
522
+ targetField: 'email',
523
+ verificationInfoKey: 'emailVerificationInfo',
524
+ storageKey: emailVerifyInfoKey,
525
+ parseError: (e, t, l) => parseEmailError(e, t, l),
526
+ interfacePrefix: '', // 邮箱不需要前缀
527
+ timerIntervalKey: 'emailTimerInterval'
528
+ });
529
+ }
530
+ },
531
+ _getEnable(settingData, id) {
532
+ const config = settingData || this.data.settingData;
533
+ return config.miniprogram?.find?.((item) => item.id === id)?.enable ?? false;
534
+ },
535
+ _intervalSender(delay, id = this.data.currentLoginType) {
536
+ if (delay <= 0) return
537
+
538
+ const keys = {
539
+ __sms: {
540
+ delay: 'smsDelay',
541
+ timer: 'timerInterval',
542
+ storageKey: phoneVerifyInfoKey
543
+ },
544
+ email: {
545
+ delay: 'emailDelay',
546
+ timer: 'emailTimerInterval',
547
+ storageKey: emailVerifyInfoKey
548
+ }
549
+ } [id]
550
+
551
+ this.setData({
552
+ [keys.delay]: delay
553
+ });
554
+
555
+ this[keys.timer] = setInterval(() => {
556
+ delay = delay - 1;
557
+ if (delay <= 0) {
558
+ clearInterval(this[keys.timer]);
559
+ }
560
+ this.setData({
561
+ [keys.delay]: delay
562
+ });
563
+ setStorage({
564
+ [keys.delay]: +new Date() + delay * 1000
565
+ }, keys.storageKey);
566
+ }, 1000);
567
+ },
568
+ async getAvatarUrl(avatarUrl) {
569
+ let avatarRealUrl = avatarUrl
570
+ if (avatarUrl.startsWith('cloud://')) {
571
+ const tcb = await app.cloud.getCloudInstance()
572
+ const pic = await tcb.getTempFileURL({
573
+ fileList: [avatarUrl]
574
+ })
575
+ avatarRealUrl = pic?.fileList?.[0]?.tempFileURL || ''
576
+ }
577
+
578
+ return avatarRealUrl;
579
+ },
580
+ async dealOpenIdOrUnionIdLogin(id) {
581
+ try {
582
+ const res = await (AUTO_LOGIN_METHOD[id])(this);
583
+ if (res) {
584
+ const {
585
+ avatarUrl,
586
+ nickName
587
+ } = app.auth.currentUser
588
+ const {
589
+ SDKVersion
590
+ } = wx.getSystemInfoSync();
425
591
  // 2.21.2以下版本不支持获取用户头像/昵称,暂时跳过
426
- if (!this.data.settingData?.access?.disabledCollectUserInfo && (!avatarUrl || !nickName) && this.compareVersion(SDKVersion, '2.21.2') >= 0) {
427
- const avatarRealUrl = await this.getAvatarUrl(avatarUrl)
428
- this.setData({
429
- avatarRealUrl,
430
- avatarUrl,
431
- nickName,
432
- baseInfoShow: true
433
- })
434
- } else {
435
- loginSuccessCallBack(this);
436
- }
437
- }
438
- } catch (error) {
439
-
440
- }
441
- },
442
- jumpBaseInfoConfig() {
443
- loginSuccessCallBack(this);
444
- },
445
- methodClick(e) {
446
- const {
447
- type
448
- } = e.currentTarget.dataset.item || {};
449
-
450
- const currentLoginType = type || this.data.currentLoginType;
451
-
452
- this.setData({
453
- currentLoginType,
454
- currentLoginList: this.data.settingData.miniprogram.filter(v => v.type !== currentLoginType)
455
- })
456
-
457
- if (AUTO_LOGIN_TYPE.includes(currentLoginType)) {
458
- if (this.data.disableLoginSubmit) {
459
- wx.showToast({
460
- title: '请先阅读并同意协议',
461
- icon: 'none'
462
- })
463
- return;
464
- };
465
- this.dealOpenIdOrUnionIdLogin(currentLoginType)
466
- return;
467
- }
468
- },
469
- onChooseAvatar(e) {
470
- this.setData({
471
- avatarUrl: e.detail.avatarUrl,
472
- avatarRealUrl: '',
473
- })
474
- },
475
- nickNameChange(e) {
476
- this.setData({
477
- nickName: e.detail.value
478
- })
479
- },
480
- randomStr(len = 32) {
481
- const s = [];
482
- const hexDigits = '0123456789abcdef';
483
- const dictLen = hexDigits.length;
484
- for (var i = 0; i < len; i++) {
485
- const index = Math.floor(Math.random() * dictLen);
486
- s[i] = hexDigits[index];
487
- }
488
- const result = s.join('');
489
- return result;
490
- },
491
- async uploadFile(file) {
492
- return new Promise(async (resolve) => {
493
- const uploadPath = 'weda-uploader';
494
- const fileType = file.split('.')?.slice(-1)?.[0];
495
- const cloudPath = `${uploadPath}/${this.randomStr()}-${Date.now()}.${fileType}`;
496
- try {
497
- const tcb = await app.cloud.getCloudInstance();
498
- const res = await tcb.uploadFile({
499
- cloudPath: cloudPath,
500
- filePath: file,
501
- });
502
- if (res.fileID) {
503
- resolve(res)
504
- } else {
505
- throw res
506
- }
507
- } catch (e) {
508
- wx.showModal({
509
- title: '上传失败,请重试',
510
- content: e.message,
511
- showCancel: false,
512
- });
513
- resolve({});;
514
- }
515
- })
516
- },
517
- async baseInfoConfirm() {
518
- let {
519
- avatarUrl,
520
- nickName,
521
- currentLoginType,
522
- } = this.data
523
-
524
- wx.showLoading()
525
-
526
- if (avatarUrl && !avatarUrl.startsWith('cloud://')) {
527
- const res = await this.uploadFile(avatarUrl)
528
- avatarUrl = res.fileID || ''
529
- }
530
-
531
- try {
532
- const tcb = await app.cloud.getCloudInstance();
533
- await tcb.auth.updateUserBasicInfo({
534
- avatar_url: avatarUrl,
535
- nickname: nickName,
536
- user_id: app.auth.currentUser.userId
537
- });
538
- await (AUTO_LOGIN_METHOD[currentLoginType])(this);
539
- loginSuccessCallBack(this);
540
- } catch (error) {
541
- wx.showModal({
542
- title: '更新用户信息出错',
543
- content: error.message,
544
- showCancel: false,
545
- });
546
- }
547
-
548
- wx.hideLoading()
549
- },
592
+ if (!this.data.settingData?.access?.disabledCollectUserInfo && (!avatarUrl || !nickName) && compareVersion(SDKVersion, '2.21.2') >= 0) {
593
+ const avatarRealUrl = await this.getAvatarUrl(avatarUrl)
594
+ this.setData({
595
+ avatarRealUrl,
596
+ avatarUrl,
597
+ nickName,
598
+ baseInfoShow: true
599
+ })
600
+ } else {
601
+ loginSuccessCallBack(this);
602
+ }
603
+ }
604
+ } catch (error) {
605
+
606
+ }
607
+ },
608
+ jumpBaseInfoConfig() {
609
+ loginSuccessCallBack(this);
610
+ },
611
+ methodClick(e) {
612
+ const {
613
+ id
614
+ } = e.currentTarget.dataset.item || {};
615
+
616
+ const currentLoginType = id || this.data.currentLoginType;
617
+
618
+ this.setData({
619
+ currentLoginType,
620
+ })
621
+
622
+ if (AUTO_LOGIN_TYPE.includes(currentLoginType)) {
623
+ if (this.data.disableLoginSubmit) {
624
+ wx.showToast({
625
+ title: '请先阅读并同意协议',
626
+ icon: 'none'
627
+ })
628
+ return;
629
+ };
630
+ this.dealOpenIdOrUnionIdLogin(currentLoginType)
631
+ return;
632
+ }
633
+ },
634
+ onChooseAvatar(e) {
635
+ this.setData({
636
+ avatarUrl: e.detail.avatarUrl,
637
+ avatarRealUrl: '',
638
+ })
639
+ },
640
+ nickNameChange(e) {
641
+ this.setData({
642
+ nickName: e.detail.value
643
+ })
644
+ },
645
+ async uploadFile(file) {
646
+ return new Promise(async (resolve) => {
647
+ const uploadPath = 'weda-uploader';
648
+ const fileType = file.split('.')?.slice(-1)?.[0];
649
+ const cloudPath = `${uploadPath}/${randomStr()}-${Date.now()}.${fileType}`;
650
+ try {
651
+ const tcb = await app.cloud.getCloudInstance();
652
+ const res = await tcb.uploadFile({
653
+ cloudPath: cloudPath,
654
+ filePath: file,
655
+ });
656
+ if (res.fileID) {
657
+ resolve(res)
658
+ } else {
659
+ throw res
660
+ }
661
+ } catch (e) {
662
+ wx.showModal({
663
+ title: '上传失败,请重试',
664
+ content: e.message,
665
+ showCancel: false,
666
+ });
667
+ resolve({});;
668
+ }
669
+ })
670
+ },
671
+ async baseInfoConfirm() {
672
+ let {
673
+ avatarUrl,
674
+ nickName,
675
+ currentLoginType,
676
+ } = this.data
677
+
678
+ wx.showLoading()
679
+
680
+ if (avatarUrl && !avatarUrl.startsWith('cloud://')) {
681
+ const res = await this.uploadFile(avatarUrl)
682
+ avatarUrl = res.fileID || ''
683
+ }
684
+
685
+ try {
686
+ const tcb = await app.cloud.getCloudInstance();
687
+ await tcb.auth.updateUserBasicInfo({
688
+ avatar_url: avatarUrl,
689
+ nickname: nickName,
690
+ user_id: app.auth.currentUser.userId
691
+ });
692
+ await (AUTO_LOGIN_METHOD[currentLoginType])(this);
693
+ loginSuccessCallBack(this);
694
+ } catch (error) {
695
+ wx.showModal({
696
+ title: '更新用户信息出错',
697
+ content: error.message,
698
+ showCancel: false,
699
+ });
700
+ }
701
+
702
+ wx.hideLoading()
703
+ },
550
704
  loginCancel() {
551
705
  if (this.data.publicPage?.length <= 0) return;
552
706
 
@@ -569,21 +723,28 @@ Component({
569
723
  packageName,
570
724
  })
571
725
  }
726
+ },
727
+ toRegister() {
728
+ const isRegister = !this.data.isRegister
729
+ this.setData({
730
+ isRegister,
731
+ currentLoginType: isRegister ? this.data.registerMethods[0].id : this.data.currentLoginList.filter(v => !AUTO_LOGIN_TYPE.includes(v.id))?.[0]?.id
732
+ })
572
733
  }
573
- },
574
- observers: {
575
- 'settingData,agreement': function (settingData, agreement) {
576
- const disabled = settingData?.agreement?.enable && !agreement;
577
- if (!!this.data.disableLoginSubmit !== disabled) {
578
- this.setData({
579
- disableLoginSubmit: disabled
580
- });
581
- }
582
- },
583
- disableLoginSubmit: function (disableLoginSubmit) {
584
- this.setData({
585
- loginButtonClass: `${BUTTON_CLASSNAME} ${disableLoginSubmit ? 'is-disabled' : ''}`,
586
- });
587
- },
588
- },
734
+ },
735
+ observers: {
736
+ 'settingData,agreement': function (settingData, agreement) {
737
+ const disabled = settingData?.agreement?.enable && !agreement;
738
+ if (!!this.data.disableLoginSubmit !== disabled) {
739
+ this.setData({
740
+ disableLoginSubmit: disabled
741
+ });
742
+ }
743
+ },
744
+ disableLoginSubmit: function (disableLoginSubmit) {
745
+ this.setData({
746
+ loginButtonClass: `${BUTTON_CLASSNAME} ${disableLoginSubmit ? 'is-disabled' : ''}`,
747
+ });
748
+ },
749
+ },
589
750
  });