@tdesign/uniapp 0.7.3 → 0.8.1

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 (258) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +44 -35
  3. package/dist/action-sheet/README.md +9 -4
  4. package/dist/action-sheet/action-sheet.vue +158 -150
  5. package/dist/action-sheet/props.ts +2 -2
  6. package/dist/action-sheet/type.ts +1 -1
  7. package/dist/avatar/README.md +3 -1
  8. package/dist/avatar/avatar.vue +89 -87
  9. package/dist/avatar-group/avatar-group.vue +69 -67
  10. package/dist/back-top/README.md +3 -1
  11. package/dist/back-top/back-top.vue +60 -58
  12. package/dist/badge/README.md +3 -1
  13. package/dist/badge/badge.vue +69 -59
  14. package/dist/button/README.md +3 -1
  15. package/dist/button/button.vue +121 -116
  16. package/dist/button/props.ts +2 -2
  17. package/dist/button/type.ts +1 -1
  18. package/dist/calendar/README.md +3 -1
  19. package/dist/calendar/calendar-header.vue +4 -4
  20. package/dist/calendar/calendar.vue +308 -297
  21. package/dist/calendar/template.vue +1 -1
  22. package/dist/cascader/README.en-US.md +2 -1
  23. package/dist/cascader/README.md +5 -2
  24. package/dist/cascader/cascader.vue +340 -328
  25. package/dist/cascader/props.ts +6 -1
  26. package/dist/cascader/type.ts +6 -0
  27. package/dist/cell/README.md +3 -1
  28. package/dist/cell/cell.vue +127 -121
  29. package/dist/cell-group/cell-group.vue +32 -30
  30. package/dist/check-tag/check-tag.vue +73 -71
  31. package/dist/checkbox/README.md +3 -1
  32. package/dist/checkbox/checkbox.vue +127 -127
  33. package/dist/checkbox/props.ts +6 -6
  34. package/dist/checkbox/type.ts +3 -3
  35. package/dist/checkbox-group/checkbox-group.vue +175 -173
  36. package/dist/checkbox-group/props.ts +6 -6
  37. package/dist/checkbox-group/type.ts +4 -4
  38. package/dist/col/README.md +3 -1
  39. package/dist/col/col.vue +26 -24
  40. package/dist/collapse/README.md +3 -1
  41. package/dist/collapse/collapse.vue +83 -81
  42. package/dist/collapse-panel/collapse-panel.vue +121 -119
  43. package/dist/collapse-panel/props.ts +4 -4
  44. package/dist/collapse-panel/type.ts +2 -2
  45. package/dist/color-picker/README.md +4 -2
  46. package/dist/color-picker/color-picker.vue +324 -322
  47. package/dist/color-picker/props.ts +2 -2
  48. package/dist/color-picker/template.vue +14 -10
  49. package/dist/common/common.ts +122 -5
  50. package/dist/common/src/index.js +0 -1
  51. package/dist/common/style/theme/index.css +5 -5
  52. package/dist/common/utils.js +7 -2
  53. package/dist/common/validator.ts +496 -0
  54. package/dist/config-provider/README.en-US.md +184 -0
  55. package/dist/config-provider/README.md +236 -0
  56. package/dist/config-provider/config-provider.vue +105 -0
  57. package/dist/config-provider/config-store.js +70 -0
  58. package/dist/config-provider/props.ts +16 -0
  59. package/dist/config-provider/reactive-state.js +39 -0
  60. package/dist/config-provider/type.ts +401 -0
  61. package/dist/config-provider/use-config.js +29 -0
  62. package/dist/config-provider/utils.js +29 -0
  63. package/dist/count-down/README.md +3 -1
  64. package/dist/count-down/count-down.vue +98 -97
  65. package/dist/date-time-picker/README.md +3 -1
  66. package/dist/date-time-picker/date-time-picker.vue +410 -395
  67. package/dist/demo/demo.vue +1 -0
  68. package/dist/dialog/README.md +3 -1
  69. package/dist/dialog/dialog.vue +175 -173
  70. package/dist/divider/README.md +3 -1
  71. package/dist/divider/divider.vue +38 -36
  72. package/dist/draggable/draggable.vue +60 -58
  73. package/dist/drawer/README.md +4 -2
  74. package/dist/drawer/drawer.vue +48 -46
  75. package/dist/dropdown-item/dropdown-item.vue +209 -207
  76. package/dist/dropdown-item/props.ts +4 -4
  77. package/dist/dropdown-item/type.ts +3 -3
  78. package/dist/dropdown-menu/README.md +2 -2
  79. package/dist/dropdown-menu/dropdown-menu.vue +93 -99
  80. package/dist/empty/README.md +3 -1
  81. package/dist/empty/empty.vue +43 -42
  82. package/dist/fab/README.md +3 -1
  83. package/dist/fab/fab.vue +88 -86
  84. package/dist/footer/README.md +3 -1
  85. package/dist/footer/footer.vue +36 -34
  86. package/dist/form/README.en-US.md +17 -24
  87. package/dist/form/README.md +21 -26
  88. package/dist/form/form.css +1 -166
  89. package/dist/form/form.vue +251 -236
  90. package/dist/form/props.ts +2 -21
  91. package/dist/form/type.ts +6 -69
  92. package/dist/form-item/README.en-US.md +4 -5
  93. package/dist/form-item/README.md +4 -5
  94. package/dist/form-item/form-item.css +72 -95
  95. package/dist/form-item/form-item.vue +315 -337
  96. package/dist/form-item/form-model.ts +125 -173
  97. package/dist/form-item/props.ts +4 -17
  98. package/dist/form-item/type.ts +43 -1
  99. package/dist/grid/README.md +3 -1
  100. package/dist/grid/grid.vue +53 -51
  101. package/dist/grid-item/grid-item.vue +121 -119
  102. package/dist/guide/README.md +4 -2
  103. package/dist/guide/guide.vue +281 -277
  104. package/dist/icon/README.md +12 -11
  105. package/dist/icon/icon.css +1633 -1624
  106. package/dist/icon/icon.vue +78 -76
  107. package/dist/image/README.md +4 -2
  108. package/dist/image/image.vue +103 -101
  109. package/dist/image-viewer/README.md +3 -1
  110. package/dist/image-viewer/image-viewer.vue +160 -158
  111. package/dist/image-viewer/props.ts +2 -2
  112. package/dist/image-viewer/type.ts +1 -1
  113. package/dist/index.js +16 -0
  114. package/dist/indexes/README.md +3 -1
  115. package/dist/indexes/indexes.vue +264 -267
  116. package/dist/indexes-anchor/indexes-anchor.vue +41 -41
  117. package/dist/input/README.md +3 -1
  118. package/dist/input/input.vue +172 -198
  119. package/dist/input/props.ts +6 -6
  120. package/dist/input/type.ts +3 -3
  121. package/dist/link/README.md +3 -1
  122. package/dist/link/link.vue +73 -71
  123. package/dist/loading/README.md +3 -1
  124. package/dist/loading/loading.vue +59 -59
  125. package/dist/locale/ar_KW.ts +157 -0
  126. package/dist/locale/en_US.ts +146 -0
  127. package/dist/locale/it_IT.ts +145 -0
  128. package/dist/locale/ja_JP.ts +132 -0
  129. package/dist/locale/ko_KR.ts +132 -0
  130. package/dist/locale/ru_RU.ts +157 -0
  131. package/dist/locale/zh_CN.ts +133 -0
  132. package/dist/locale/zh_TW.ts +132 -0
  133. package/dist/message/README.md +8 -3
  134. package/dist/message/message.vue +181 -173
  135. package/dist/message/props.ts +2 -2
  136. package/dist/message/type.ts +1 -1
  137. package/dist/message-item/message-item.vue +192 -184
  138. package/dist/mixins/page-scroll.d.ts +19 -0
  139. package/dist/mixins/skyline.js +1 -1
  140. package/dist/mixins/using-config.js +39 -0
  141. package/dist/navbar/README.md +3 -1
  142. package/dist/navbar/navbar.vue +201 -199
  143. package/dist/notice-bar/README.md +3 -1
  144. package/dist/notice-bar/notice-bar.vue +175 -171
  145. package/dist/notice-bar/props.ts +2 -2
  146. package/dist/notice-bar/type.ts +1 -1
  147. package/dist/npm/dayjs/esm/locale/ar.js +81 -0
  148. package/dist/npm/dayjs/esm/locale/it.js +39 -0
  149. package/dist/overlay/README.md +3 -1
  150. package/dist/overlay/overlay.vue +50 -48
  151. package/dist/picker/README.md +3 -1
  152. package/dist/picker/picker.vue +168 -161
  153. package/dist/picker-item/picker-item.vue +269 -269
  154. package/dist/popover/README.md +4 -2
  155. package/dist/popover/popover.vue +262 -261
  156. package/dist/popover/props.ts +4 -4
  157. package/dist/popover/type.ts +2 -2
  158. package/dist/popup/README.md +3 -1
  159. package/dist/popup/popup.vue +46 -45
  160. package/dist/progress/README.md +3 -3
  161. package/dist/progress/progress.vue +76 -76
  162. package/dist/pull-down-refresh/README.md +3 -1
  163. package/dist/pull-down-refresh/props.ts +2 -2
  164. package/dist/pull-down-refresh/pull-down-refresh.vue +240 -234
  165. package/dist/pull-down-refresh/type.ts +1 -1
  166. package/dist/qrcode/README.md +4 -2
  167. package/dist/qrcode/components/qrcode-canvas/qrcode-canvas.vue +340 -338
  168. package/dist/qrcode/components/qrcode-status/qrcode-status.vue +6 -6
  169. package/dist/qrcode/qrcode.vue +93 -87
  170. package/dist/radio/README.md +3 -1
  171. package/dist/radio/props.ts +6 -6
  172. package/dist/radio/radio.vue +118 -120
  173. package/dist/radio/type.ts +3 -3
  174. package/dist/radio-group/props.ts +4 -4
  175. package/dist/radio-group/radio-group.vue +136 -134
  176. package/dist/radio-group/type.ts +4 -4
  177. package/dist/rate/README.md +3 -1
  178. package/dist/rate/computed.js +2 -2
  179. package/dist/rate/props.ts +4 -4
  180. package/dist/rate/rate.css +1 -0
  181. package/dist/rate/rate.vue +155 -154
  182. package/dist/rate/type.ts +2 -2
  183. package/dist/result/README.md +3 -1
  184. package/dist/result/result.vue +41 -39
  185. package/dist/row/row.vue +38 -36
  186. package/dist/script/postinstall.js +49 -24
  187. package/dist/scroll-view/scroll-view.vue +24 -22
  188. package/dist/search/README.md +3 -1
  189. package/dist/search/props.ts +2 -2
  190. package/dist/search/search.vue +127 -125
  191. package/dist/search/type.ts +1 -1
  192. package/dist/side-bar/README.md +3 -1
  193. package/dist/side-bar/side-bar.vue +57 -55
  194. package/dist/side-bar-item/side-bar-item.vue +86 -86
  195. package/dist/skeleton/README.md +3 -1
  196. package/dist/skeleton/skeleton.vue +126 -124
  197. package/dist/slider/README.md +4 -2
  198. package/dist/slider/props.ts +2 -2
  199. package/dist/slider/slider.vue +457 -457
  200. package/dist/slider/type.ts +1 -1
  201. package/dist/step-item/step-item.vue +77 -75
  202. package/dist/stepper/README.md +3 -1
  203. package/dist/stepper/props.ts +2 -2
  204. package/dist/stepper/stepper.vue +168 -149
  205. package/dist/stepper/type.ts +1 -1
  206. package/dist/steps/README.md +3 -1
  207. package/dist/steps/props.ts +2 -2
  208. package/dist/steps/steps.vue +83 -81
  209. package/dist/steps/type.ts +1 -1
  210. package/dist/sticky/README.md +3 -1
  211. package/dist/sticky/sticky.vue +104 -102
  212. package/dist/swipe-cell/README.md +3 -1
  213. package/dist/swipe-cell/swipe-cell.vue +91 -89
  214. package/dist/swiper/README.md +4 -2
  215. package/dist/swiper/swiper.vue +91 -89
  216. package/dist/swiper-nav/swiper-nav.vue +38 -36
  217. package/dist/switch/README.md +3 -1
  218. package/dist/switch/props.ts +2 -2
  219. package/dist/switch/switch.vue +62 -62
  220. package/dist/switch/type.ts +1 -1
  221. package/dist/tab-bar/README.md +3 -1
  222. package/dist/tab-bar/tab-bar.vue +88 -86
  223. package/dist/tab-bar-item/tab-bar-item.vue +85 -82
  224. package/dist/tab-panel/tab-panel.vue +66 -64
  225. package/dist/tabs/README.md +3 -1
  226. package/dist/tabs/tabs.vue +294 -287
  227. package/dist/tag/README.md +3 -1
  228. package/dist/tag/tag.vue +79 -77
  229. package/dist/textarea/README.md +3 -1
  230. package/dist/textarea/props.ts +6 -6
  231. package/dist/textarea/textarea.vue +128 -151
  232. package/dist/textarea/type.ts +3 -3
  233. package/dist/theme.css +467 -0
  234. package/dist/theme.css.d.ts +2 -0
  235. package/dist/theme.less +1 -0
  236. package/dist/theme.less.d.ts +2 -0
  237. package/dist/toast/README.md +3 -1
  238. package/dist/toast/toast.vue +107 -106
  239. package/dist/transition/README.md +4 -6
  240. package/dist/transition/transition.vue +30 -28
  241. package/dist/tree-select/README.md +3 -1
  242. package/dist/tree-select/tree-select.vue +151 -151
  243. package/dist/types/config-provider.d.ts +7 -0
  244. package/dist/types/index.d.ts +17 -0
  245. package/dist/types/popover.d.ts +7 -0
  246. package/dist/upload/README.en-US.md +13 -14
  247. package/dist/upload/README.md +14 -13
  248. package/dist/upload/props.ts +2 -4
  249. package/dist/upload/type.ts +12 -11
  250. package/dist/upload/upload.css +1 -1
  251. package/dist/upload/upload.vue +672 -512
  252. package/dist/watermark/README.md +3 -1
  253. package/dist/watermark/watermark.vue +151 -149
  254. package/global.d.ts +2 -0
  255. package/package.json +51 -5
  256. package/dist/common/src/superComponent.js +0 -5
  257. package/dist/common/validator.js +0 -38
  258. package/dist/form/form-item-props.ts +0 -56
@@ -0,0 +1,496 @@
1
+ import type { IsEmailOptions, IsURLOptions } from './common';
2
+
3
+ export function isFunction(val: unknown): val is (...args: unknown[]) => unknown {
4
+ return typeof val === 'function';
5
+ }
6
+
7
+ export const isString = (val: unknown): val is string => typeof val === 'string';
8
+
9
+ export const isNull = (value: unknown): value is null => value === null;
10
+
11
+ export const isUndefined = (value: unknown): value is undefined => value === undefined;
12
+
13
+ export function isDef<T>(value: T | undefined | null): value is T {
14
+ return !isUndefined(value) && !isNull(value);
15
+ }
16
+
17
+ export function isInteger(value: unknown): boolean {
18
+ return Number.isInteger(value);
19
+ }
20
+
21
+ export function isNumeric(value: unknown): boolean {
22
+ return !Number.isNaN(Number(value));
23
+ }
24
+
25
+ export function isNumber(value: unknown): value is number {
26
+ return typeof value === 'number';
27
+ }
28
+
29
+ export function isBoolean(value: unknown): value is boolean {
30
+ return typeof value === 'boolean';
31
+ }
32
+
33
+ export function isObject(x: unknown): x is Record<string, unknown> {
34
+ const type = typeof x;
35
+ return x !== null && (type === 'object' || type === 'function');
36
+ }
37
+
38
+ export function isPlainObject(val: unknown): val is Record<string, unknown> {
39
+ return val !== null && typeof val === 'object' && Object.prototype.toString.call(val) === '[object Object]';
40
+ }
41
+
42
+ export function isEmpty(val: unknown): boolean {
43
+ if (val === null || val === undefined) return true;
44
+ if (typeof val === 'string' || Array.isArray(val)) return val.length === 0;
45
+ if (val instanceof Map || val instanceof Set) return val.size === 0;
46
+ if (typeof val === 'object') return Object.keys(val).length === 0;
47
+ return true;
48
+ }
49
+
50
+ /**
51
+ * 日期校验选项
52
+ */
53
+ export interface IsDateOptions {
54
+ /**
55
+ * 日期格式
56
+ * @default 'YYYY/MM/DD'
57
+ */
58
+ format?: string;
59
+ /**
60
+ * 日期分隔符
61
+ * @default ['/', '-']
62
+ */
63
+ delimiters?: string[];
64
+ /**
65
+ * 是否启用严格模式
66
+ * @default false
67
+ */
68
+ strictMode?: boolean;
69
+ }
70
+
71
+ /**
72
+ * 验证是否为有效日期
73
+ * 支持字符串格式(YYYY/MM/DD、YYYY-MM-DD 等)和 Date 对象
74
+ */
75
+ export function isDate(input: string | Date, options?: IsDateOptions): boolean {
76
+ const defaultOptions: Required<IsDateOptions> = {
77
+ format: 'YYYY/MM/DD',
78
+ delimiters: ['/', '-'],
79
+ strictMode: false,
80
+ };
81
+ const opts = { ...defaultOptions, ...options };
82
+
83
+ if (typeof input === 'string') {
84
+ const delimiter = opts.delimiters.find(d => opts.format.includes(d));
85
+ if (!delimiter) return false;
86
+
87
+ const formatParts = opts.format.split(delimiter);
88
+ const dateParts = input.split(delimiter);
89
+ if (formatParts.length !== dateParts.length) return false;
90
+
91
+ let year = '';
92
+ let month = '';
93
+ let day = '';
94
+ for (let i = 0; i < formatParts.length; i += 1) {
95
+ const fmt = formatParts[i].toUpperCase();
96
+ const val = dateParts[i];
97
+ if (fmt.includes('Y')) year = val;
98
+ else if (fmt.includes('M')) month = val;
99
+ else if (fmt.includes('D')) day = val;
100
+ }
101
+
102
+ if (month.length === 1) month = `0${month}`;
103
+ if (day.length === 1) day = `0${day}`;
104
+
105
+ if (year.length === 2) {
106
+ const currentYearSuffix = new Date().getFullYear() % 100;
107
+ year = Number(year) <= currentYearSuffix ? `20${year}` : `19${year}`;
108
+ }
109
+
110
+ const date = new Date(`${year}-${month}-${day}T00:00:00.000Z`);
111
+ return (
112
+ date.getUTCFullYear() === Number(year)
113
+ && date.getUTCMonth() + 1 === Number(month)
114
+ && date.getUTCDate() === Number(day)
115
+ );
116
+ }
117
+
118
+ if (!opts.strictMode) {
119
+ if (Object.prototype.toString.call(input) === '[object Date]' && Number.isFinite((input as Date).getTime())) {
120
+ return true;
121
+ }
122
+ }
123
+
124
+ return false;
125
+ }
126
+
127
+ /**
128
+ * 检查主机名是否匹配黑/白名单
129
+ */
130
+ function checkHost(host: string, list: Array<string | RegExp>): boolean {
131
+ if (!list?.length) return false;
132
+ return list.some((item) => {
133
+ if (item instanceof RegExp) return item.test(host);
134
+ return host === item;
135
+ });
136
+ }
137
+
138
+ /**
139
+ * 计算字符串的字节长度(UTF-8 编码)
140
+ */
141
+ function byteLength(str: string): number {
142
+ return encodeURI(str).split(/%..|./).length - 1;
143
+ }
144
+
145
+ /**
146
+ * FQDN 校验选项
147
+ */
148
+ interface IsFQDNOptions {
149
+ /**
150
+ * 是否要求顶级域名
151
+ * @default true
152
+ */
153
+ require_tld?: boolean;
154
+ /**
155
+ * 是否允许下划线
156
+ * @default false
157
+ */
158
+ allow_underscores?: boolean;
159
+ /**
160
+ * 是否允许末尾的点
161
+ * @default false
162
+ */
163
+ allow_trailing_dot?: boolean;
164
+ }
165
+
166
+ /**
167
+ * 检查是否为 FQDN(完全限定域名)
168
+ */
169
+ function isFQDN(str: string, options: IsFQDNOptions = {}): boolean {
170
+ const opts: Required<IsFQDNOptions> = {
171
+ require_tld: true,
172
+ allow_underscores: false,
173
+ allow_trailing_dot: false,
174
+ ...options,
175
+ };
176
+
177
+ let domain = str;
178
+ if (opts.allow_trailing_dot && domain[domain.length - 1] === '.') {
179
+ domain = domain.substring(0, domain.length - 1);
180
+ }
181
+
182
+ const parts = domain.split('.');
183
+ const tld = parts[parts.length - 1];
184
+
185
+ if (opts.require_tld) {
186
+ if (parts.length < 2) return false;
187
+ if (!/^([a-z\u00A1-\u00A8\u00AA-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{2,}|xn[a-z0-9-]{2,})$/i.test(tld)) {
188
+ return false;
189
+ }
190
+ if (/\s/.test(tld)) return false;
191
+ }
192
+
193
+ if (/^\d+$/.test(tld)) return false;
194
+
195
+ return parts.every((part) => {
196
+ if (part.length > 63) return false;
197
+ if (!/^[a-z_\u00a1-\uffff0-9-]+$/i.test(part)) return false;
198
+ // 禁止全角字符
199
+ if (/[\uff01-\uff5e]/.test(part)) return false;
200
+ // 不能以连字符开头或结尾
201
+ if (/^-|-$/.test(part)) return false;
202
+ if (!opts.allow_underscores && /_/.test(part)) return false;
203
+ return true;
204
+ });
205
+ }
206
+
207
+ /**
208
+ * 检查是否为有效的 IPv4 或 IPv6 地址
209
+ */
210
+ function isIP(ipAddress: string, version?: number): boolean {
211
+ const IPv4SegmentFormat = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
212
+ const IPv4AddressFormat = `(${IPv4SegmentFormat}[.]){3}${IPv4SegmentFormat}`;
213
+ const IPv4AddressRegExp = new RegExp(`^${IPv4AddressFormat}$`);
214
+ const IPv6SegmentFormat = '(?:[0-9a-fA-F]{1,4})';
215
+ const IPv6AddressRegExp = new RegExp('^('
216
+ + `(?:${IPv6SegmentFormat}:){7}(?:${IPv6SegmentFormat}|:)|`
217
+ + `(?:${IPv6SegmentFormat}:){6}(?:${IPv4AddressFormat}|:${IPv6SegmentFormat}|:)|`
218
+ + `(?:${IPv6SegmentFormat}:){5}(?::${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,2}|:)|`
219
+ + `(?:${IPv6SegmentFormat}:){4}(?:(:${IPv6SegmentFormat}){0,1}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,3}|:)|`
220
+ + `(?:${IPv6SegmentFormat}:){3}(?:(:${IPv6SegmentFormat}){0,2}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,4}|:)|`
221
+ + `(?:${IPv6SegmentFormat}:){2}(?:(:${IPv6SegmentFormat}){0,3}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,5}|:)|`
222
+ + `(?:${IPv6SegmentFormat}:){1}(?:(:${IPv6SegmentFormat}){0,4}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,6}|:)|`
223
+ + `(?::((?::${IPv6SegmentFormat}){0,5}:${IPv4AddressFormat}|(?::${IPv6SegmentFormat}){1,7}|:))`
224
+ + ')(%[0-9a-zA-Z.]{1,})?$');
225
+
226
+ const v = version ? version.toString() : '';
227
+ if (!v) {
228
+ return isIP(ipAddress, 4) || isIP(ipAddress, 6);
229
+ }
230
+ if (v === '4') return IPv4AddressRegExp.test(ipAddress);
231
+ if (v === '6') return IPv6AddressRegExp.test(ipAddress);
232
+ return false;
233
+ }
234
+
235
+ /**
236
+ * 验证是否为有效邮箱地址
237
+ * 参考 validator.js 的 isEmail 实现,支持 options 参数
238
+ */
239
+ export function isEmail(str: string, options?: IsEmailOptions): boolean {
240
+ if (typeof str !== 'string') return false;
241
+
242
+ let email = str;
243
+
244
+ const defaultOpts: Required<IsEmailOptions> = {
245
+ allow_display_name: false,
246
+ allow_underscores: false,
247
+ require_display_name: false,
248
+ allow_utf8_local_part: true,
249
+ require_tld: true,
250
+ blacklisted_chars: '',
251
+ ignore_max_length: false,
252
+ host_blacklist: [],
253
+ host_whitelist: [],
254
+ allow_ip_domain: false,
255
+ domain_specific_validation: false,
256
+ };
257
+ const opts = { ...defaultOpts, ...options };
258
+
259
+ // 处理 Display Name 格式,如 "John" <john@example.com>
260
+ // eslint-disable-next-line no-control-regex
261
+ const splitNameAddress = /^([^\x00-\x1F\x7F-\x9F\cX]+)</i;
262
+ if (opts.require_display_name || opts.allow_display_name) {
263
+ const displayEmail = email.match(splitNameAddress);
264
+ if (displayEmail) {
265
+ let displayName = displayEmail[1];
266
+ email = email.replace(displayName, '').replace(/(^<|>$)/g, '');
267
+ if (displayName.endsWith(' ')) {
268
+ displayName = displayName.slice(0, -1);
269
+ }
270
+ // 简单校验 display name
271
+ const nameWithoutQuotes = displayName.replace(/^"(.+)"$/, '$1');
272
+ if (!nameWithoutQuotes.trim()) return false;
273
+ if (/[.";<>]/.test(nameWithoutQuotes) && nameWithoutQuotes === displayName) return false;
274
+ } else if (opts.require_display_name) {
275
+ return false;
276
+ }
277
+ }
278
+
279
+ if (!opts.ignore_max_length && email.length > 254) return false;
280
+
281
+ const parts = email.split('@');
282
+ const domain = parts.pop()!;
283
+ const user = parts.join('@');
284
+
285
+ if (!user || !domain) return false;
286
+
287
+ const lowerDomain = domain.toLowerCase();
288
+
289
+ // 域名黑名单
290
+ if (opts.host_blacklist.length > 0 && checkHost(lowerDomain, opts.host_blacklist)) {
291
+ return false;
292
+ }
293
+ // 域名白名单
294
+ if (opts.host_whitelist.length > 0 && !checkHost(lowerDomain, opts.host_whitelist)) {
295
+ return false;
296
+ }
297
+
298
+ // Gmail 特殊校验
299
+ if (opts.domain_specific_validation && (lowerDomain === 'gmail.com' || lowerDomain === 'googlemail.com')) {
300
+ const username = user.toLowerCase().split('+')[0];
301
+ if (byteLength(username.replace(/\./g, '')) < 6 || byteLength(username.replace(/\./g, '')) > 30) {
302
+ return false;
303
+ }
304
+ const gmailUserPart = /^[a-z\d]+$/;
305
+ const gmailParts = username.split('.');
306
+ for (let i = 0; i < gmailParts.length; i++) {
307
+ if (!gmailUserPart.test(gmailParts[i])) return false;
308
+ }
309
+ }
310
+
311
+ // 字节长度校验
312
+ if (!opts.ignore_max_length && (byteLength(user) > 64 || byteLength(domain) > 254)) {
313
+ return false;
314
+ }
315
+
316
+ // 域名校验:先尝试 FQDN,再尝试 IP
317
+ if (!isFQDN(domain, { require_tld: opts.require_tld, allow_underscores: opts.allow_underscores })) {
318
+ if (!opts.allow_ip_domain) return false;
319
+ if (!isIP(domain)) {
320
+ if (!domain.startsWith('[') || !domain.endsWith(']')) return false;
321
+ const noBracketDomain = domain.slice(1, -1);
322
+ if (!noBracketDomain || !isIP(noBracketDomain)) return false;
323
+ }
324
+ }
325
+
326
+ // 黑名单字符
327
+ if (opts.blacklisted_chars && new RegExp(`[${opts.blacklisted_chars}]+`, 'g').test(user)) {
328
+ return false;
329
+ }
330
+
331
+ // 引号包裹的用户名
332
+ if (user[0] === '"' && user[user.length - 1] === '"') {
333
+ const innerUser = user.slice(1, user.length - 1);
334
+ /* eslint-disable no-control-regex */
335
+ if (opts.allow_utf8_local_part) {
336
+ return /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*$/i.test(innerUser);
337
+ }
338
+ return /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f]))*$/i.test(innerUser);
339
+ /* eslint-enable no-control-regex */
340
+ }
341
+
342
+ // 用户名逐段校验(按 . 分割)
343
+ /* eslint-disable no-useless-escape */
344
+ const pattern = opts.allow_utf8_local_part
345
+ ? /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\u00A1-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+$/i
346
+ : /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~]+$/i;
347
+ /* eslint-enable no-useless-escape */
348
+ const userParts = user.split('.');
349
+ for (let i = 0; i < userParts.length; i++) {
350
+ if (!pattern.test(userParts[i])) return false;
351
+ }
352
+
353
+ return true;
354
+ }
355
+
356
+ /**
357
+ * 验证是否为有效 URL
358
+ * 参考 validator.js 的 isURL 实现,支持完整 options 参数
359
+ */
360
+ export function isURL(str: string, options?: IsURLOptions): boolean {
361
+ if (typeof str !== 'string') return false;
362
+ if (!str || /[\s<>]/.test(str)) return false;
363
+ if (str.indexOf('mailto:') === 0) return false;
364
+
365
+ const defaultOpts: Required<IsURLOptions> = {
366
+ protocols: ['http', 'https', 'ftp'],
367
+ require_tld: true,
368
+ require_protocol: false,
369
+ require_host: true,
370
+ require_port: false,
371
+ require_valid_protocol: true,
372
+ allow_underscores: false,
373
+ allow_trailing_dot: false,
374
+ allow_protocol_relative_urls: false,
375
+ allow_fragments: true,
376
+ allow_query_components: true,
377
+ disallow_auth: false,
378
+ validate_length: true,
379
+ max_allowed_length: 2084,
380
+ host_whitelist: [],
381
+ host_blacklist: [],
382
+ };
383
+ const opts = { ...defaultOpts, ...options };
384
+
385
+ if (opts.validate_length && opts.max_allowed_length && str.length > opts.max_allowed_length) {
386
+ return false;
387
+ }
388
+
389
+ if (!opts.allow_fragments && str.includes('#')) {
390
+ return false;
391
+ }
392
+
393
+ if (!opts.allow_query_components && (str.includes('?') || str.includes('&'))) {
394
+ return false;
395
+ }
396
+
397
+ let url = str;
398
+ let protocol: string | undefined;
399
+
400
+ // 协议解析
401
+ const protocolRegex = /^([a-z][a-z0-9+\-.]*):\/\//i;
402
+ const protocolMatch = url.match(protocolRegex);
403
+ if (protocolMatch) {
404
+ protocol = protocolMatch[1].toLowerCase();
405
+ if (opts.require_valid_protocol && !opts.protocols.includes(protocol)) return false;
406
+ url = url.slice(protocolMatch[0].length);
407
+ } else if (opts.require_protocol) {
408
+ if (opts.allow_protocol_relative_urls && str.startsWith('//')) {
409
+ url = url.slice(2);
410
+ } else {
411
+ return false;
412
+ }
413
+ } else if (str.startsWith('//')) {
414
+ if (!opts.allow_protocol_relative_urls) return false;
415
+ url = url.slice(2);
416
+ }
417
+
418
+ if (url === '') return false;
419
+
420
+ // 分离片段和查询参数
421
+ let split: string[] = url.split('#');
422
+ url = split.shift()!;
423
+ split = url.split('?');
424
+ url = split.shift()!;
425
+
426
+ // 分离路径
427
+ split = url.split('/');
428
+ url = split.shift()!;
429
+
430
+ if (url === '' && !opts.require_host) return true;
431
+
432
+ // 分离认证信息
433
+ split = url.split('@');
434
+ if (split.length > 1) {
435
+ if (opts.disallow_auth) return false;
436
+ const auth = split.shift()!;
437
+ if (auth === '') return false;
438
+ if (auth.indexOf(':') >= 0 && auth.split(':').length > 2) return false;
439
+ const [authUser, authPassword] = auth.split(':');
440
+ if (authUser === '' && authPassword === '') return false;
441
+ }
442
+
443
+ const hostname = split.join('@');
444
+ let portStr: string | null = null;
445
+ let ipv6: string | null = null;
446
+ let host: string;
447
+
448
+ // IPv6 包裹格式 [::1]:port
449
+ const wrappedIpv6 = /^\[([^\]]+)\](?::([0-9]+))?$/;
450
+ const ipv6Match = hostname.match(wrappedIpv6);
451
+ if (ipv6Match) {
452
+ host = '';
453
+ [, ipv6] = ipv6Match;
454
+ portStr = ipv6Match[2] || null;
455
+ } else {
456
+ const hostSplit = hostname.split(':');
457
+ [host] = hostSplit;
458
+ const restParts = hostSplit.slice(1);
459
+ if (restParts.length) {
460
+ portStr = restParts.join(':');
461
+ }
462
+ }
463
+
464
+ // 端口校验
465
+ if (portStr !== null && portStr.length > 0) {
466
+ const port = parseInt(portStr, 10);
467
+ if (!/^[0-9]+$/.test(portStr) || port <= 0 || port > 65535) return false;
468
+ } else if (opts.require_port) {
469
+ return false;
470
+ }
471
+
472
+ // 白名单优先
473
+ if (opts?.host_whitelist?.length) {
474
+ return checkHost(host, opts.host_whitelist);
475
+ }
476
+
477
+ if (host === '' && !opts.require_host) return true;
478
+
479
+ // 域名校验
480
+ if (!isIP(host) && !isFQDN(host, {
481
+ require_tld: opts.require_tld,
482
+ allow_underscores: opts.allow_underscores,
483
+ allow_trailing_dot: opts.allow_trailing_dot,
484
+ }) && (!ipv6 || !isIP(ipv6, 6))) {
485
+ return false;
486
+ }
487
+
488
+ host = host || ipv6!;
489
+
490
+ // 黑名单
491
+ if (opts?.host_blacklist?.length && checkHost(host, opts.host_blacklist)) {
492
+ return false;
493
+ }
494
+
495
+ return true;
496
+ }
@@ -0,0 +1,184 @@
1
+ :: BASE_DOC ::
2
+
3
+ ## API
4
+
5
+ ### ConfigProvider Props
6
+
7
+ name | type | default | description | required
8
+ -- | -- | -- | -- | --
9
+ custom-style | Object | - | CSS(Cascading Style Sheets) | N
10
+ global-config | Object | - | global config。Typescript: `GlobalConfigProvider` | N
11
+ theme-vars | Object | - | theme Variables | N
12
+
13
+ ### GlobalConfigProvider
14
+
15
+ name | type | default | description | required
16
+ -- | -- | -- | -- | --
17
+ action-sheet | Object | - | ActionSheet global configs。Typescript: `ActionSheetConfig` | N
18
+ calendar | Object | - | Calendar global configs。Typescript: `CalendarConfig` | N
19
+ cascader | Object | - | Cascader global configs。Typescript: `CascaderConfig` | N
20
+ class-prefix | String | t | \- | N
21
+ date-time-picker | Object | - | DateTimePicker global configs。Typescript: `DateTimePickerConfig` | N
22
+ dropdown-menu | Object | - | DropdownMenu global configs。Typescript: `DropdownMenuConfig` | N
23
+ guide | Object | - | Guide global configs。Typescript: `GuideConfig` | N
24
+ picker | Object | - | Picker global configs。Typescript: `PickerConfig` | N
25
+ pull-down-refresh | Object | - | PullDownRefresh global configs。Typescript: `PullDownRefreshConfig` | N
26
+ qrcode | Object | - | QRCode global configs。Typescript: `QRCodeConfig` | N
27
+ rate | Object | - | Rate global configs。Typescript: `RateConfig` | N
28
+ tab-bar | Object | - | TabBar global configs。Typescript: `TabBarConfig` | N
29
+ upload | Object | - | Upload global configs。Typescript: `UploadConfig` | N
30
+
31
+ ### ActionSheetConfig
32
+
33
+ name | type | default | description | required
34
+ -- | -- | -- | -- | --
35
+ cancel | String | - | cancel text | N
36
+
37
+ ### AttachmentsConfig
38
+
39
+ name | type | default | description | required
40
+ -- | -- | -- | -- | --
41
+ status | Object | - | Typescript: `{ pending: string; fail: string; }` | N
42
+
43
+ ### CalendarConfig
44
+
45
+ name | type | default | description | required
46
+ -- | -- | -- | -- | --
47
+ confirm | String | - | confirm text | N
48
+ month-title | String | - | \- | N
49
+ months | Array | - | Typescript: `string[]` | N
50
+ title | String | - | \- | N
51
+ weekdays | Array | - | Typescript: `string[]` | N
52
+
53
+ ### CascaderConfig
54
+
55
+ name | type | default | description | required
56
+ -- | -- | -- | -- | --
57
+ placeholder | String | - | \- | N
58
+ title | String | - | \- | N
59
+
60
+ ### ChatActionbarConfig
61
+
62
+ name | type | default | description | required
63
+ -- | -- | -- | -- | --
64
+ action-bar | Object | - | Typescript: `{ replay: string; copy: string; good: string; bad: string; share: string; quote: string; }` | N
65
+
66
+ ### ChatSenderConfig
67
+
68
+ name | type | default | description | required
69
+ -- | -- | -- | -- | --
70
+ placeholder | String | - | \- | N
71
+ send-text | String | - | \- | N
72
+ stop-text | String | - | \- | N
73
+
74
+ ### ChatThinkingConfig
75
+
76
+ name | type | default | description | required
77
+ -- | -- | -- | -- | --
78
+ status | Object | - | Typescript: `{ pending: string; complete: string; stop: string; }` | N
79
+
80
+ ### DateTimePickerConfig
81
+
82
+ name | type | default | description | required
83
+ -- | -- | -- | -- | --
84
+ cancel | String | - | \- | N
85
+ confirm | String | - | \- | N
86
+ date-label | String | - | \- | N
87
+ format | String | 'YYYY-MM-DD HH:mm:ss' | \- | N
88
+ hour-label | String | - | \- | N
89
+ minute-label | String | - | \- | N
90
+ month-label | String | - | \- | N
91
+ second-label | String | - | \- | N
92
+ title | String | - | \- | N
93
+ year-label | String | - | \- | N
94
+
95
+ ### DropdownMenuConfig
96
+
97
+ name | type | default | description | required
98
+ -- | -- | -- | -- | --
99
+ confirm | String | - | confirm text | N
100
+ reset | String | - | reset text | N
101
+
102
+ ### FormConfig
103
+
104
+ name | type | default | description | required
105
+ -- | -- | -- | -- | --
106
+ colon-text | String | - | colon on the right of label ":" | N
107
+ error-message | Object | - | Typescript: `FormErrorMessage`,[Form API Documents](./form?tab=api)。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/config-provider/type.ts) | N
108
+ required-mark | Boolean | true | \- | N
109
+ required-mark-position | String | left | Display position of required symbols。options: left/right | N
110
+
111
+ ### GuideConfig
112
+
113
+ name | type | default | description | required
114
+ -- | -- | -- | -- | --
115
+ back | String | - | \- | N
116
+ finish | String | - | \- | N
117
+ next | String | - | \- | N
118
+ skip | String | - | \- | N
119
+
120
+ ### ImageConfig
121
+
122
+ name | type | default | description | required
123
+ -- | -- | -- | -- | --
124
+ error-text | String | - | loading text, default value is "Error" | N
125
+ loading-text | String | - | loading text, default value is "loading" | N
126
+ replace-image-src | Function | - | replace all `src` attribute of images。Typescript: `(params: ImageProps) => string`,[Image API Documents](./image?tab=api)。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/config-provider/type.ts) | N
127
+
128
+ ### InputConfig
129
+
130
+ name | type | default | description | required
131
+ -- | -- | -- | -- | --
132
+ placeholder | String | - | \- | N
133
+
134
+ ### PickerConfig
135
+
136
+ name | type | default | description | required
137
+ -- | -- | -- | -- | --
138
+ cancel | String | - | cancel text | N
139
+ confirm | String | - | confirm text | N
140
+
141
+ ### PullDownRefreshConfig
142
+
143
+ name | type | default | description | required
144
+ -- | -- | -- | -- | --
145
+ loading-texts | Array | - | Typescript: `string[]` | N
146
+
147
+ ### QRCodeConfig
148
+
149
+ name | type | default | description | required
150
+ -- | -- | -- | -- | --
151
+ expired-text | String | - | Language configuration, "QR code expired" description text | N
152
+ refresh-text | String | - | Language configuration, "QR code refresh" description text | N
153
+ scanned-text | String | - | Language configuration, "QR code scanned" description text | N
154
+
155
+ ### RateConfig
156
+
157
+ name | type | default | description | required
158
+ -- | -- | -- | -- | --
159
+ no-value-text | String | - | \- | N
160
+ value-text | String | - | \- | N
161
+
162
+ ### TabBarConfig
163
+
164
+ name | type | default | description | required
165
+ -- | -- | -- | -- | --
166
+ have-more-news-aria-label | String | - | \- | N
167
+ have-news-aria-label | String | - | \- | N
168
+ more-news-aria-label | String | - | \- | N
169
+ news-aria-label | String | - | \- | N
170
+
171
+ ### UploadConfig
172
+
173
+ name | type | default | description | required
174
+ -- | -- | -- | -- | --
175
+ progress | Object | - | Typescript: `UploadConfigProgress` | N
176
+
177
+ ### UploadConfigProgress
178
+
179
+ name | type | default | description | required
180
+ -- | -- | -- | -- | --
181
+ fail-text | String | - | \- | N
182
+ success-text | String | - | \- | N
183
+ uploading-text | String | - | \- | N
184
+ waiting-text | String | - | \- | N