@norcy/react-native-toolkit 0.1.16 → 0.1.18

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.
Files changed (66) hide show
  1. package/lib/commonjs/ConfigDataModel.js +122 -0
  2. package/lib/commonjs/ConfigDataModel.js.map +1 -0
  3. package/lib/commonjs/DevConfig.js +9 -5
  4. package/lib/commonjs/DevConfig.js.map +1 -1
  5. package/lib/commonjs/Frequence.js +67 -0
  6. package/lib/commonjs/Frequence.js.map +1 -0
  7. package/lib/commonjs/LoginManager.js +21 -18
  8. package/lib/commonjs/LoginManager.js.map +1 -1
  9. package/lib/commonjs/MessageModel.js +157 -0
  10. package/lib/commonjs/MessageModel.js.map +1 -0
  11. package/lib/commonjs/PrefData.js +10 -0
  12. package/lib/commonjs/PrefData.js.map +1 -1
  13. package/lib/commonjs/SentryManager.js +3 -3
  14. package/lib/commonjs/SentryManager.js.map +1 -1
  15. package/lib/commonjs/Tool.js +52 -0
  16. package/lib/commonjs/Tool.js.map +1 -0
  17. package/lib/commonjs/VipAndroidManager.js +154 -0
  18. package/lib/commonjs/VipAndroidManager.js.map +1 -0
  19. package/lib/commonjs/VipManager.js +280 -0
  20. package/lib/commonjs/VipManager.js.map +1 -0
  21. package/lib/commonjs/index.js +72 -0
  22. package/lib/commonjs/index.js.map +1 -1
  23. package/lib/module/ConfigDataModel.js +117 -0
  24. package/lib/module/ConfigDataModel.js.map +1 -0
  25. package/lib/module/DevConfig.js +5 -1
  26. package/lib/module/DevConfig.js.map +1 -1
  27. package/lib/module/Frequence.js +61 -0
  28. package/lib/module/Frequence.js.map +1 -0
  29. package/lib/module/LoginManager.js +5 -2
  30. package/lib/module/LoginManager.js.map +1 -1
  31. package/lib/module/MessageModel.js +151 -0
  32. package/lib/module/MessageModel.js.map +1 -0
  33. package/lib/module/PrefData.js +10 -0
  34. package/lib/module/PrefData.js.map +1 -1
  35. package/lib/module/SentryManager.js +1 -1
  36. package/lib/module/SentryManager.js.map +1 -1
  37. package/lib/module/Tool.js +46 -0
  38. package/lib/module/Tool.js.map +1 -0
  39. package/lib/module/VipAndroidManager.js +145 -0
  40. package/lib/module/VipAndroidManager.js.map +1 -0
  41. package/lib/module/VipManager.js +273 -0
  42. package/lib/module/VipManager.js.map +1 -0
  43. package/lib/module/index.js +6 -0
  44. package/lib/module/index.js.map +1 -1
  45. package/lib/typescript/ConfigDataModel.d.ts +17 -0
  46. package/lib/typescript/DevConfig.d.ts +1 -1
  47. package/lib/typescript/Frequence.d.ts +10 -0
  48. package/lib/typescript/LoginManager.d.ts +2 -3
  49. package/lib/typescript/MessageModel.d.ts +26 -0
  50. package/lib/typescript/Tool.d.ts +7 -0
  51. package/lib/typescript/VipAndroidManager.d.ts +28 -0
  52. package/lib/typescript/VipManager.d.ts +29 -0
  53. package/lib/typescript/index.d.ts +6 -0
  54. package/package.json +5 -3
  55. package/src/ConfigDataModel.ts +146 -0
  56. package/src/DevConfig.ts +4 -1
  57. package/src/Frequence.ts +70 -0
  58. package/src/LoginManager.ts +6 -9
  59. package/src/MessageModel.ts +191 -0
  60. package/src/PrefData.ts +10 -0
  61. package/src/SentryManager.ts +2 -1
  62. package/src/Tool.ts +64 -0
  63. package/src/VipAndroidManager.ts +110 -0
  64. package/src/VipManager.ts +282 -0
  65. package/src/index.tsx +6 -0
  66. package/ios/ReactNativeToolkit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -4
@@ -4,12 +4,18 @@ type ReactNativeToolkitType = {
4
4
  declare const _default: ReactNativeToolkitType;
5
5
  export default _default;
6
6
  export * from './AppleLoginUtil';
7
+ export * from './ConfigDataModel';
7
8
  export * from './DevConfig';
9
+ export * from './Frequence';
8
10
  export * from './LoginManager';
11
+ export * from './MessageModel';
9
12
  export * from './Notification';
10
13
  export * from './PrefData';
11
14
  export * from './ReportUtil';
12
15
  export * from './SentryManager';
13
16
  export * from './SyncPrefData';
17
+ export * from './Tool';
18
+ export * from './VipAndroidManager';
19
+ export * from './VipManager';
14
20
  export * from './WeChatLoginUtil';
15
21
  export * from './constant';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norcy/react-native-toolkit",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "My Toolkit",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -67,7 +67,8 @@
67
67
  "leancloud-storage": "^4.10.1",
68
68
  "@react-native-community/async-storage": "^1.12.0",
69
69
  "i18n-js": "^4.2.2",
70
- "react-native-wechat-lib": "^1.1.26"
70
+ "react-native-wechat-lib": "^1.1.26",
71
+ "react-native-purchases": "5.6.0"
71
72
  },
72
73
  "peerDependencies": {
73
74
  "react": "*",
@@ -78,7 +79,8 @@
78
79
  "leancloud-storage": "*",
79
80
  "@react-native-community/async-storage": "*",
80
81
  "i18n-js": "*",
81
- "react-native-wechat-lib": "*"
82
+ "react-native-wechat-lib": "*",
83
+ "react-native-purchases": "*"
82
84
  },
83
85
  "jest": {
84
86
  "preset": "react-native",
@@ -0,0 +1,146 @@
1
+ import { Object } from 'leancloud-storage';
2
+ import { Platform } from 'react-native';
3
+ import { getReadableVersion } from 'react-native-device-info';
4
+ import { DevConfig } from './DevConfig';
5
+ import { BuildInPrefs } from './PrefData';
6
+ import { VipManager } from './VipManager';
7
+
8
+ const EventEmitter = require('events').EventEmitter;
9
+ const eventEmitter = new EventEmitter();
10
+ const semverGt = require('semver/functions/gt');
11
+ const semverGte = require('semver/functions/gte');
12
+ const semverCoerce = require('semver/functions/coerce');
13
+
14
+ export interface ConfigsType {
15
+ [key: string]: any;
16
+ }
17
+ let MyItems: ConfigsType = {};
18
+ let VersionInfo = {
19
+ currentVersion: semverCoerce(getReadableVersion()).version,
20
+ latestVersion: semverCoerce(getReadableVersion()).version,
21
+ isDanger: true,
22
+ hasNewVersion: false,
23
+ };
24
+
25
+ const AV = require('leancloud-storage');
26
+
27
+ const _filterByVersion = (items: Object[]) => {
28
+ let ret = [];
29
+ const curVer = semverCoerce(getReadableVersion()).version;
30
+ for (const item of items) {
31
+ const minVer = item.get('minVer')?.trim();
32
+ const maxVer = item.get('maxVer')?.trim();
33
+ // 确保 cur 处于 [minVer, maxVer)
34
+ if (minVer?.length && semverGt(semverCoerce(minVer).version, curVer)) {
35
+ continue;
36
+ }
37
+ if (maxVer?.length && semverGte(curVer, semverCoerce(maxVer).version)) {
38
+ continue;
39
+ }
40
+
41
+ ret.push(item);
42
+ }
43
+ return ret;
44
+ };
45
+
46
+ const _filterByVip = (items: Object[]) => {
47
+ let ret = [];
48
+ const isVip = VipManager.isVip();
49
+ const isForeverVip = VipManager.isForeverVip();
50
+ for (const item of items) {
51
+ const vipFilter = item.get('vipFilter');
52
+ if (
53
+ vipFilter === 0 ||
54
+ (vipFilter === 1 && !isVip) ||
55
+ (vipFilter === 2 && !isForeverVip)
56
+ ) {
57
+ ret.push(item);
58
+ }
59
+ }
60
+ return ret;
61
+ };
62
+
63
+ const _updateKeyValues = (items: Object[]) => {
64
+ for (const item of items) {
65
+ let value = item.get('value');
66
+ if (typeof value === 'string') {
67
+ // 因为 leancloud 无法配置空白内容,所以 trim 下,这样就能以空格代替空白{
68
+ value = value.trim();
69
+ }
70
+ MyItems[item.get('key')] = value;
71
+ }
72
+ };
73
+
74
+ const handleVersionInfo = () => {
75
+ // online > current,则更新 latestVersion
76
+ let key = 'ov';
77
+ if (Platform.OS === 'android') {
78
+ key = 'aov';
79
+ }
80
+ const configVersion = ConfigDataModel.get(key);
81
+ if (configVersion) {
82
+ if (
83
+ semverGt(semverCoerce(configVersion).version, VersionInfo.latestVersion)
84
+ ) {
85
+ VersionInfo.latestVersion = semverCoerce(configVersion).version;
86
+
87
+ VersionInfo.hasNewVersion = true;
88
+ }
89
+ // current <= online,才安全 ,防止忘记修改 online
90
+ if (
91
+ !semverGt(VersionInfo.currentVersion, semverCoerce(configVersion).version)
92
+ ) {
93
+ VersionInfo.isDanger = false;
94
+ }
95
+ }
96
+
97
+ if (__DEV__) {
98
+ if (DevConfig.hasValue(BuildInPrefs.DevDanger)) {
99
+ VersionInfo.isDanger = DevConfig.getValue(BuildInPrefs.DevDanger);
100
+ }
101
+ }
102
+ };
103
+
104
+ const ConfigDataModel = {
105
+ init: (defaultValues: ConfigsType) => {
106
+ MyItems = defaultValues;
107
+ },
108
+ fetch: () => {
109
+ const query = new AV.Query('Configs');
110
+ query.equalTo('valid', true).equalTo(Platform.OS, true);
111
+ query
112
+ .find()
113
+ .then((items: Object[]) => {
114
+ // 根据版本过滤
115
+ items = _filterByVersion(items);
116
+ // 根据 VIP 状态过滤
117
+ items = _filterByVip(items);
118
+ _updateKeyValues(items);
119
+ // 处理 Version 信息
120
+ handleVersionInfo();
121
+ console.log('Config 抓取完毕');
122
+ ConfigDataModel.emitChange();
123
+ })
124
+ .catch((e: any) => {
125
+ console.error(e);
126
+ });
127
+ },
128
+
129
+ getVersionConfig: () => {
130
+ return VersionInfo;
131
+ },
132
+
133
+ get: (key: string) => {
134
+ return MyItems[key];
135
+ },
136
+
137
+ emitChange: () => {
138
+ eventEmitter.emit('change');
139
+ },
140
+
141
+ addListenerOnce: (callback: Function) => {
142
+ eventEmitter.once('change', callback);
143
+ },
144
+ };
145
+
146
+ export { ConfigDataModel };
package/src/DevConfig.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BuildInPrefs, PrefData, PrefType } from '@norcy/react-native-toolkit';
1
+ import { BuildInPrefs, PrefData, PrefType } from './PrefData';
2
2
 
3
3
  export interface DevConfigType {
4
4
  label: string;
@@ -46,6 +46,9 @@ export const DevConfig = {
46
46
  },
47
47
 
48
48
  getConfig: (pref: PrefType) => {
49
+ if (pref?.key?.length === 0) {
50
+ return [];
51
+ }
49
52
  return DevConfigs[pref.key];
50
53
  },
51
54
  };
@@ -0,0 +1,70 @@
1
+ import { BuildInPrefs, PrefData } from './PrefData';
2
+
3
+ export interface FreConfigType {
4
+ key: string;
5
+ limit?: number; // 0 表示无限次提示
6
+ gap?: number; // 单位:秒;0 表示不间断提示
7
+ }
8
+
9
+ class FreConfigValueType {
10
+ lastTimestamp: number; // 上一次展示的时间,配合 gap 计算
11
+ count: number; // 实际展示的次数,配合 limit 计算
12
+ constructor() {
13
+ this.count = 0;
14
+ this.lastTimestamp = 0;
15
+ }
16
+ }
17
+
18
+ const now = () => {
19
+ return new Date().getTime() / 1000;
20
+ };
21
+
22
+ export const FrequenceManager = {
23
+ canRun: (config: FreConfigType) => {
24
+ const configs: { [key: string]: FreConfigValueType } =
25
+ PrefData.getValue(BuildInPrefs.FrequenceConfig) ?? {};
26
+ if (!configs[config.key]) {
27
+ configs[config.key] = new FreConfigValueType();
28
+ }
29
+ const configValue: FreConfigValueType = configs[config.key];
30
+
31
+ // 优先判断 Count
32
+ if ((config.limit ?? 0) > 0 && configValue.count >= (config.limit ?? 0)) {
33
+ console.log('[Frequence] 次数限制', configValue, config);
34
+ return false;
35
+ }
36
+ // 再判断 Time
37
+ if (
38
+ (config.gap ?? 0) > 0 &&
39
+ now() - configValue.lastTimestamp < (config.gap ?? 0)
40
+ ) {
41
+ console.log('[Frequence] 时间限制', configValue, config);
42
+ return false;
43
+ }
44
+
45
+ console.log('[Frequence] 无限制', configValue, config);
46
+ return true;
47
+ },
48
+
49
+ markFinishForCount: (config: FreConfigType) => {
50
+ const configs: { [key: string]: FreConfigValueType } =
51
+ PrefData.getValue(BuildInPrefs.FrequenceConfig) ?? {};
52
+ if (!configs[config.key]) {
53
+ configs[config.key] = new FreConfigValueType();
54
+ }
55
+ configs[config.key].count++;
56
+ // console.log('[Frequence] mark finish', configs[config.key]);
57
+ PrefData.setValue(BuildInPrefs.FrequenceConfig, configs);
58
+ },
59
+
60
+ markFinishForTime: (configKey: string) => {
61
+ const configs: { [key: string]: FreConfigValueType } =
62
+ PrefData.getValue(BuildInPrefs.FrequenceConfig) ?? {};
63
+ if (!configs[configKey]) {
64
+ configs[configKey] = new FreConfigValueType();
65
+ }
66
+ configs[configKey].lastTimestamp = now();
67
+ // console.log('[Frequence] mark finish', configs[config.key]);
68
+ PrefData.setValue(BuildInPrefs.FrequenceConfig, configs);
69
+ },
70
+ };
@@ -1,17 +1,14 @@
1
- import {
2
- AppleLoginUtil,
3
- BuildInPrefs,
4
- LoginType,
5
- Notification,
6
- PrefData,
7
- WeChatLoginUtil,
8
- } from '@norcy/react-native-toolkit';
9
1
  import { User } from 'leancloud-storage';
10
2
  import { Platform } from 'react-native';
11
3
  import { getUniqueIdSync } from 'react-native-device-info';
4
+ import { AppleLoginUtil } from './AppleLoginUtil';
5
+ import { Notification } from './Notification';
6
+ import { BuildInPrefs, PrefData } from './PrefData';
7
+ import { WeChatLoginUtil } from './WeChatLoginUtil';
12
8
  import {
13
9
  LoginAuthDataType,
14
10
  LoginState,
11
+ LoginType,
15
12
  LoginUserDataType,
16
13
  UserType,
17
14
  } from './constant';
@@ -288,7 +285,7 @@ export const LoginManager = {
288
285
 
289
286
  batchUpdateUser: async (
290
287
  keys: string[],
291
- values: string[],
288
+ values: (string | Date)[],
292
289
  callback?: Function
293
290
  ) => {
294
291
  console.log('Batch Update User Begin: ' + keys + ' ' + values);
@@ -0,0 +1,191 @@
1
+ import { Platform } from 'react-native';
2
+ import { getReadableVersion } from 'react-native-device-info';
3
+ import { ConfigDataModel } from './ConfigDataModel';
4
+ import { FreConfigType, FrequenceManager } from './Frequence';
5
+ import { LoginManager } from './LoginManager';
6
+ import { BuildInPrefs, PrefData } from './PrefData';
7
+ import { Tool } from './Tool';
8
+ import { VipManager } from './VipManager';
9
+
10
+ const AV = require('leancloud-storage');
11
+ const EventEmitter = require('events').EventEmitter;
12
+ const eventEmitter = new EventEmitter();
13
+ const semverGt = require('semver/functions/gt');
14
+ const semverGte = require('semver/functions/gte');
15
+ const semverCoerce = require('semver/functions/coerce');
16
+
17
+ interface Message {
18
+ objectId: string;
19
+ android: boolean;
20
+ ios: boolean;
21
+ title?: string;
22
+ content?: string;
23
+ maxVer?: string;
24
+ minVer?: string;
25
+ key?: string;
26
+ updatedAt: string;
27
+ valid: boolean;
28
+ vipFilter?: number;
29
+ isRead: boolean;
30
+ users?: string[];
31
+ limit?: number;
32
+ gap?: number;
33
+ }
34
+
35
+ let MyMessages: Message[] = [];
36
+
37
+ const _filterByVersion = (items: Message[]): Message[] => {
38
+ let ret: Message[] = [];
39
+ const curVer = semverCoerce(getReadableVersion()).version;
40
+ for (const item of items) {
41
+ const minVer = item.minVer?.trim();
42
+ const maxVer = item.maxVer?.trim();
43
+ // 确保 cur 处于 [minVer, maxVer)
44
+ if (minVer?.length && semverGt(semverCoerce(minVer).version, curVer)) {
45
+ continue;
46
+ }
47
+ if (maxVer?.length && semverGte(curVer, semverCoerce(maxVer).version)) {
48
+ continue;
49
+ }
50
+
51
+ ret.push(item);
52
+ }
53
+ return ret;
54
+ };
55
+
56
+ const _filterByVip = (items: Message[]): Message[] => {
57
+ let ret: Message[] = [];
58
+ const isVip = VipManager.isVip();
59
+ const isForeverVip = VipManager.isForeverVip();
60
+ for (const item of items) {
61
+ const vipFilter = item.vipFilter;
62
+ if (
63
+ vipFilter === 0 ||
64
+ (vipFilter === 1 && !isVip) ||
65
+ (vipFilter === 2 && !isForeverVip)
66
+ ) {
67
+ ret.push(item);
68
+ }
69
+ }
70
+ return ret;
71
+ };
72
+
73
+ const _filterByUser = (items: Message[]): Message[] => {
74
+ let ret: Message[] = [];
75
+ for (const item of items) {
76
+ const users = item.users;
77
+ if (users?.length) {
78
+ if (LoginManager.isLogin()) {
79
+ if (
80
+ users?.includes(LoginManager.currentUser()?.AVUser.get('objectId'))
81
+ ) {
82
+ ret.push(item);
83
+ }
84
+ }
85
+ } else {
86
+ ret.push(item);
87
+ }
88
+ }
89
+ return ret;
90
+ };
91
+
92
+ const _handleReadMessages = (items: Message[]): Message[] => {
93
+ if (__DEV__) {
94
+ // return items;
95
+ }
96
+
97
+ // 根据已读状态标为已读
98
+ const readMsgs = PrefData.getValue(BuildInPrefs.Messages);
99
+ for (const item of items) {
100
+ if (readMsgs.includes(item.objectId)) {
101
+ item.isRead = true;
102
+ }
103
+ }
104
+
105
+ // 挑选需要强制未读
106
+ for (const item of items) {
107
+ if (!item.isRead) {
108
+ continue;
109
+ }
110
+ if (item.key === 'changeLog') {
111
+ // 强制升级弹窗
112
+ // Message 是在 Config 回包之后再请求的,因此此时 Config 的值一定是准确的
113
+ const config = ConfigDataModel.getVersionConfig();
114
+ if (ConfigDataModel.get('upgrade_alert') && config.hasNewVersion) {
115
+ item.isRead = false;
116
+ item.updatedAt = new Date().toString();
117
+ }
118
+ } else if (item.key?.length) {
119
+ const freConfig: FreConfigType = {
120
+ key: item.key,
121
+ limit: item.limit,
122
+ gap: item.gap,
123
+ };
124
+ if (FrequenceManager.canRun(freConfig)) {
125
+ FrequenceManager.markFinishForTime(freConfig.key);
126
+ item.isRead = false;
127
+ item.updatedAt = new Date().toString();
128
+ }
129
+ }
130
+ }
131
+
132
+ return items;
133
+ };
134
+
135
+ export const MessageModel = {
136
+ fetch: () => {
137
+ const query = new AV.Query('Message');
138
+ query
139
+ .equalTo('valid', true)
140
+ .equalTo(Platform.OS, true)
141
+ .descending('updatedAt');
142
+ query
143
+ .find()
144
+ .then(async (items: any[]) => {
145
+ console.log('Message 抓取完毕');
146
+ MyMessages = items.map((item) => Tool.jsonify(item));
147
+ MyMessages = _filterByVersion(MyMessages);
148
+ MyMessages = _filterByVip(MyMessages);
149
+ MyMessages = _filterByUser(MyMessages);
150
+ MyMessages = _handleReadMessages(MyMessages);
151
+
152
+ // 由于强制插入的未读消息更改了时间,因此需要重新排序
153
+ MyMessages = MyMessages.sort((a: Message, b: Message) => {
154
+ let dateA = new Date(a.updatedAt).getTime();
155
+ let dateB = new Date(b.updatedAt).getTime();
156
+ return dateB - dateA;
157
+ });
158
+ MessageModel.emitChange();
159
+ })
160
+ .catch((e: any) => {
161
+ console.error(e);
162
+ });
163
+ },
164
+
165
+ getUnreadMessages: (): Message[] => {
166
+ return MyMessages.filter((item) => !item.isRead);
167
+ },
168
+
169
+ getAllMessages: (): Message[] => {
170
+ return MyMessages;
171
+ },
172
+
173
+ markMessageRead: async (message: Message) => {
174
+ if (message.isRead || !message.objectId) {
175
+ return;
176
+ }
177
+ message.isRead = true;
178
+ const readMsgs = PrefData.getValue(BuildInPrefs.Messages);
179
+ readMsgs.push(message.objectId);
180
+ await PrefData.setValue(BuildInPrefs.Messages, readMsgs);
181
+ MessageModel.emitChange();
182
+ },
183
+
184
+ emitChange: () => {
185
+ eventEmitter.emit('change', MyMessages.slice());
186
+ },
187
+
188
+ addListenerOnce: (callback: Function) => {
189
+ eventEmitter.once('change', callback);
190
+ },
191
+ };
package/src/PrefData.ts CHANGED
@@ -33,6 +33,16 @@ export const BuildInPrefs: PrefsType = {
33
33
  type: 'int',
34
34
  default: 0,
35
35
  },
36
+ FrequenceConfig: {
37
+ key: 'FrequenceConfig_Key',
38
+ type: 'object',
39
+ default: {},
40
+ },
41
+ Messages: {
42
+ key: 'Message_Key5',
43
+ type: 'array',
44
+ default: [],
45
+ },
36
46
  };
37
47
 
38
48
  const getPrefByKey = (Prefs: PrefsType, key: string) => {
@@ -1,5 +1,6 @@
1
- import { Notification, UserType } from '@norcy/react-native-toolkit';
2
1
  import * as Sentry from '@sentry/react-native';
2
+ import { Notification } from './Notification';
3
+ import { UserType } from './constant';
3
4
 
4
5
  export const SentryManager = {
5
6
  init: () => {
package/src/Tool.ts ADDED
@@ -0,0 +1,64 @@
1
+ export const Tool = {
2
+ sleep: (delay: number) =>
3
+ new Promise((resolve) => setTimeout(resolve, delay)),
4
+
5
+ formatDate(date: Date, fmt: string): string {
6
+ fmt = fmt ? fmt : 'yyyy-MM-dd';
7
+ var o: { [key: string]: number } = {
8
+ 'M+': date.getMonth() + 1, //月份
9
+ 'd+': date.getDate(), //日
10
+ 'h+': date.getHours(), //小时
11
+ 'm+': date.getMinutes(), //分
12
+ 's+': date.getSeconds(), //秒
13
+ 'q+': Math.floor((date.getMonth() + 3) / 3), //季度
14
+ 'S': date.getMilliseconds(), //毫秒
15
+ };
16
+
17
+ // 获取年份
18
+ if (/(y+)/i.test(fmt)) {
19
+ fmt = fmt.replace(
20
+ RegExp.$1,
21
+ (date.getFullYear() + '').substr(4 - RegExp.$1.length)
22
+ );
23
+ }
24
+
25
+ for (var k in o) {
26
+ if (new RegExp('(' + k + ')', 'i').test(fmt)) {
27
+ fmt = fmt.replace(
28
+ RegExp.$1,
29
+ (RegExp.$1.length === 1
30
+ ? o[k]
31
+ : ('00' + o[k]).substr(('' + o[k]).length)
32
+ ).toString()
33
+ );
34
+ }
35
+ }
36
+ return fmt;
37
+ },
38
+
39
+ isPlainObject: (target: any) => {
40
+ return (
41
+ target &&
42
+ target.toString() === '[object Object]' &&
43
+ Object.getPrototypeOf(target) === Object.prototype
44
+ );
45
+ },
46
+
47
+ _jsonify: (target: any): any => {
48
+ if (target && typeof target.toJSON === 'function') return target.toJSON();
49
+ // map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一次提供的函数后的返回值
50
+ if (Array.isArray(target)) return target.map(Tool._jsonify);
51
+ return target;
52
+ },
53
+
54
+ jsonify: (target: any) =>
55
+ Tool.isPlainObject(target)
56
+ ? Object.keys(target).reduce(
57
+ (result, key) => ({
58
+ ...result,
59
+ [key]: Tool._jsonify(target[key]),
60
+ }),
61
+ {}
62
+ )
63
+ : Tool._jsonify(target),
64
+ };