@rxdrag/website-lib-core 0.0.127 → 0.0.129

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 (138) hide show
  1. package/README.md +1 -1
  2. package/index.ts +1 -1
  3. package/package.json +4 -4
  4. package/src/astro/animation.ts +146 -146
  5. package/src/astro/background.ts +82 -86
  6. package/src/astro/base.ts +7 -0
  7. package/src/astro/business.ts +13 -0
  8. package/src/astro/grid/consts.ts +80 -80
  9. package/src/astro/grid/index.ts +2 -2
  10. package/src/astro/grid/types.ts +35 -35
  11. package/src/astro/image.ts +239 -239
  12. package/src/astro/index.ts +12 -10
  13. package/src/astro/link.ts +20 -21
  14. package/src/astro/media.ts +123 -123
  15. package/src/astro/nav.ts +13 -13
  16. package/src/astro/section/index.ts +7 -12
  17. package/src/component-logic/index.ts +1 -1
  18. package/src/component-logic/link-client.ts +32 -32
  19. package/src/component-logic/link.ts +61 -61
  20. package/src/design-tokens.ts +160 -160
  21. package/src/entify/Entify.ts +111 -101
  22. package/src/entify/IEntify.ts +157 -177
  23. package/src/entify/index.ts +4 -4
  24. package/src/entify/lib/collectCategoryIds.ts +20 -20
  25. package/src/entify/lib/fulltextSearch.ts +63 -62
  26. package/src/entify/lib/langFields.ts +14 -14
  27. package/src/entify/lib/listToTree.ts +23 -23
  28. package/src/entify/lib/newAvatarQueryOptions.ts +4 -4
  29. package/src/entify/lib/newOgImageQueryOptions.ts +5 -5
  30. package/src/entify/lib/newQueryPostOptions.ts +42 -45
  31. package/src/entify/lib/newQueryProductOptions.ts +96 -98
  32. package/src/entify/lib/newQueryProductsMediaOptions.ts +28 -28
  33. package/src/entify/lib/queryAllProducts.ts +40 -40
  34. package/src/entify/lib/queryBulletin.ts +28 -16
  35. package/src/entify/lib/queryEntityList.ts +41 -41
  36. package/src/entify/lib/queryFeaturedProducts.ts +69 -68
  37. package/src/entify/lib/queryLangs.ts +36 -60
  38. package/src/entify/lib/queryLatestPosts.ts +92 -91
  39. package/src/entify/lib/queryOneEntity.ts +63 -63
  40. package/src/entify/lib/queryOneMedia.ts +27 -27
  41. package/src/entify/lib/queryOnePostById.ts +21 -21
  42. package/src/entify/lib/queryOnePostBySlug.ts +40 -39
  43. package/src/entify/lib/queryOnePostCategoryBySlug.ts +35 -35
  44. package/src/entify/lib/queryOneProductById.ts +20 -20
  45. package/src/entify/lib/queryOneProductBySlug.ts +53 -52
  46. package/src/entify/lib/queryOneProductCategoryBySlug.ts +50 -49
  47. package/src/entify/lib/queryOneTheme.ts +54 -72
  48. package/src/entify/lib/queryOneUser.ts +38 -38
  49. package/src/entify/lib/queryPostCategories.ts +67 -67
  50. package/src/entify/lib/queryPostSlugs.ts +37 -37
  51. package/src/entify/lib/queryPosts.ts +175 -174
  52. package/src/entify/lib/queryProductCategories.ts +59 -59
  53. package/src/entify/lib/queryProducts.ts +145 -144
  54. package/src/entify/lib/queryProductsInMenu.ts +45 -45
  55. package/src/entify/lib/queryTagCategories.ts +58 -58
  56. package/src/entify/lib/queryTags.ts +57 -57
  57. package/src/entify/lib/queryUserIds.ts +24 -24
  58. package/src/entify/lib/queryUserPosts.ts +80 -79
  59. package/src/entify/lib/queryWebSiteSettings.ts +28 -28
  60. package/src/entify/lib/queryWebsite.ts +43 -43
  61. package/src/entify/lib/sendEmail.ts +7 -7
  62. package/src/entify/lib/toQueryOptions.ts +19 -19
  63. package/src/entify/lib/upsertEntity.ts +8 -8
  64. package/src/entify/types/index.ts +1 -1
  65. package/src/entify/types/utils.ts +11 -6
  66. package/src/entify/types/variables.ts +0 -1
  67. package/src/entify/view-model/funcs.ts +230 -230
  68. package/src/entify/view-model/index.ts +1 -1
  69. package/src/entify/view-model/models.ts +135 -135
  70. package/src/global.d.ts +7 -7
  71. package/src/index.ts +8 -8
  72. package/src/lib/formatDate.ts +15 -15
  73. package/src/lib/index.ts +3 -3
  74. package/src/lib/pagination.ts +114 -114
  75. package/src/lib/utils.ts +135 -135
  76. package/src/react/components/Analytics/eventHandlers.ts +173 -173
  77. package/src/react/components/Analytics/index.tsx +21 -21
  78. package/src/react/components/Analytics/singleton.ts +214 -214
  79. package/src/react/components/Analytics/tracking.ts +221 -221
  80. package/src/react/components/Analytics/types.ts +60 -60
  81. package/src/react/components/Analytics/utils.ts +95 -95
  82. package/src/react/components/AttachmentIcon/index.tsx +53 -53
  83. package/src/react/components/BackgroundHlsVideoPlayer.tsx +97 -97
  84. package/src/react/components/BackgroundVideoPlayer.tsx +32 -32
  85. package/src/react/components/Bulletin.tsx +30 -30
  86. package/src/react/components/ContactForm/ContactForm.tsx +289 -289
  87. package/src/react/components/ContactForm/Input.tsx +48 -48
  88. package/src/react/components/ContactForm/Input2.tsx +59 -59
  89. package/src/react/components/ContactForm/Submit.tsx +48 -48
  90. package/src/react/components/ContactForm/TelInput.tsx +215 -215
  91. package/src/react/components/ContactForm/Textarea.tsx +48 -48
  92. package/src/react/components/ContactForm/Textarea2.tsx +89 -89
  93. package/src/react/components/ContactForm/funcs.ts +64 -64
  94. package/src/react/components/ContactForm/index.ts +7 -7
  95. package/src/react/components/ContactForm/types.ts +68 -68
  96. package/src/react/components/GoogleConsent/CookieItemPanel.tsx +80 -80
  97. package/src/react/components/GoogleConsent/CumtomizedModal.tsx +148 -148
  98. package/src/react/components/GoogleConsent/GoogleConsent.tsx +100 -100
  99. package/src/react/components/GoogleConsent/gtags.ts +67 -67
  100. package/src/react/components/GoogleConsent/index.ts +2 -2
  101. package/src/react/components/GoogleConsent/types.ts +18 -18
  102. package/src/react/components/GoogleConsent//345/217/202/350/200/203.md +4 -4
  103. package/src/react/components/Icon/index.tsx +19 -19
  104. package/src/react/components/Medias/MainMedia.tsx +257 -257
  105. package/src/react/components/Medias/Thumbnail.tsx +62 -62
  106. package/src/react/components/Medias/VideoPlayer.tsx +114 -114
  107. package/src/react/components/Medias/index.tsx +271 -271
  108. package/src/react/components/ProductCard/ProductCard.tsx +24 -24
  109. package/src/react/components/ProductCard/ProductCta/index.tsx +28 -28
  110. package/src/react/components/ProductCard/ProductCta/style.css +3 -3
  111. package/src/react/components/ProductCard/ProductDescription/index.tsx +12 -12
  112. package/src/react/components/ProductCard/ProductDescription/style.css +5 -5
  113. package/src/react/components/ProductCard/ProductMedia/index.tsx +35 -35
  114. package/src/react/components/ProductCard/ProductMedia/style.css +5 -5
  115. package/src/react/components/ProductCard/ProductTitle/index.tsx +7 -7
  116. package/src/react/components/ProductCard/ProductTitle/style.css +3 -3
  117. package/src/react/components/ProductCard/ProductView.tsx +36 -36
  118. package/src/react/components/ProductCard/index.ts +4 -4
  119. package/src/react/components/ProductCard/useQueryProduct.ts +32 -32
  120. package/src/react/components/ReactModalTrigger.tsx +28 -28
  121. package/src/react/components/ReactVideoPlayer.tsx +29 -52
  122. package/src/react/components/RichTextOutline/index.tsx +75 -75
  123. package/src/react/components/RichTextOutline/useAnchorScroll.ts +23 -23
  124. package/src/react/components/Scroller.tsx +39 -39
  125. package/src/react/components/SearchInput.tsx +21 -21
  126. package/src/react/components/Share/index.tsx +86 -86
  127. package/src/react/components/Share/socials.tsx +79 -77
  128. package/src/react/components/Share//350/265/204/346/226/231.md +7 -7
  129. package/src/react/components/ToTop.tsx +72 -72
  130. package/src/react/components/VideoPlayIcon.tsx +43 -0
  131. package/src/react/components/all.ts +38 -38
  132. package/src/react/components/index.ts +16 -16
  133. package/src/robots.ts +4 -4
  134. package/src/entify/lib/newPageMetaOptions.ts +0 -18
  135. package/src/entify/lib/newQueryPageOptions.ts +0 -14
  136. package/src/entify/lib/queryOneIcon.ts +0 -27
  137. package/src/entify/lib/queryPageBySlug.ts +0 -43
  138. package/src/entify/lib/queryPageByType.ts +0 -44
@@ -1,289 +1,289 @@
1
- import { forwardRef, useState } from "react";
2
- import { Submit } from "./Submit";
3
- import { type UploadedFile } from "./FileUpload2";
4
- import clsx from "clsx";
5
- import { encrypt } from "./funcs";
6
- import { ContactFormProps, QuoteRequest } from "./types";
7
- import { getControl, getFileUpload } from "./factory";
8
-
9
- interface FormErrors {
10
- name?: string;
11
- email?: string;
12
- message?: string;
13
- }
14
-
15
- export const ContactForm = forwardRef<HTMLDivElement, ContactFormProps>(
16
- (props, ref) => {
17
- const {
18
- submit,
19
- actionUrl = "/api/ask-for-quote",
20
- formSalt,
21
- fields = [],
22
- className,
23
- classNames,
24
- fromCta,
25
- ...rest
26
- } = props;
27
- const [formData, setFormData] = useState<QuoteRequest>({
28
- name: "",
29
- email: "",
30
- company: "",
31
- message: "",
32
- phone: "", // 初始化蜜罐字段
33
- attachments: [], // 附件 ID 列表
34
- });
35
- // 错误状态
36
- const [errors, setErrors] = useState<FormErrors>({});
37
- const [submitting, setSubmitting] = useState(false);
38
- const [isUploading, setIsUploading] = useState(false);
39
- const [submitStatus, setSubmitStatus] = useState<{
40
- success?: boolean;
41
- message?: string;
42
- }>({});
43
-
44
- // 处理输入变化
45
- const handleChange = (
46
- e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
47
- isExtends?: boolean
48
- ) => {
49
- const { name, value } = e.target;
50
- if (isExtends) {
51
- setFormData((prev) => ({
52
- ...prev,
53
- extents: {
54
- ...(prev.extends || {}),
55
- [name]: value,
56
- },
57
- }));
58
- } else {
59
- setFormData((prev) => ({
60
- ...prev,
61
- [name]: value,
62
- }));
63
- }
64
-
65
- setSubmitStatus({}); // 重置提交状态
66
-
67
- // 清除对应字段的错误
68
- if (errors[name as keyof FormErrors]) {
69
- setErrors((prev) => ({
70
- ...prev,
71
- [name]: undefined,
72
- }));
73
- }
74
- };
75
-
76
- // 处理文件上传变化(单文件)
77
- const handleFileChange = (fileInfo: UploadedFile | null) => {
78
- setFormData((prev) => ({
79
- ...prev,
80
- attachments: fileInfo ? [fileInfo] : [], // 存储完整的文件信息
81
- }));
82
- };
83
-
84
- // 处理文件上传状态变化
85
- const handleFileUploadStateChange = (uploading: boolean) => {
86
- setIsUploading(uploading);
87
- };
88
- // 验证表单
89
- const validateForm = (): boolean => {
90
- const newErrors: FormErrors = {};
91
-
92
- // if (!formData.name?.trim()) {
93
- // newErrors.name = "Please enter your name";
94
- // }
95
-
96
- if (!formData.email?.trim()) {
97
- newErrors.email = "Please enter your email";
98
- } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
99
- newErrors.email = "Please enter a valid email address";
100
- }
101
-
102
- if (!formData.message?.trim()) {
103
- newErrors.message = "Please enter your message";
104
- }
105
-
106
- // 检查蜜罐字段 - 如果填写了则表示是机器人
107
- if (formData.phone) {
108
- // 悄悄失败,不显示错误信息
109
- console.log("Honeypot triggered - likely spam submission");
110
- return false;
111
- }
112
-
113
- setErrors(newErrors);
114
- return Object.keys(newErrors).length === 0;
115
- };
116
-
117
- const handleClick = async (event: React.MouseEvent) => {
118
- event.preventDefault(); // 阻止表单默认提交行为
119
-
120
- // 验证表单
121
- if (!validateForm()) {
122
- return; // 如果验证失败,不继续提交
123
- }
124
-
125
- try {
126
- setSubmitting(true);
127
- setSubmitStatus({}); // 重置提交状态
128
- const response = await fetch(actionUrl, {
129
- method: "POST",
130
- body: JSON.stringify({
131
- ...formData,
132
- //TODO:后面改成从window 或者传入
133
- fromCta: fromCta || window?.lastCta,
134
- encryptedField: encrypt(formData.phone, formSalt),
135
- }),
136
- headers: {
137
- "X-Request-URL": window.location.href,
138
- "Content-Type": "application/json",
139
- },
140
- });
141
-
142
- if (!response.ok) {
143
- throw new Error(`Server responded with status: ${response.status}`);
144
- }
145
-
146
- const result = await response.json();
147
-
148
- setSubmitStatus({
149
- success: result.success,
150
- message: result.message,
151
- });
152
-
153
- if (result.success) {
154
- // 重置表单
155
- setFormData({
156
- name: "",
157
- email: "",
158
- company: "",
159
- message: "",
160
- phone: "",
161
- attachments: [],
162
- });
163
- window.location.href = "/thanks";
164
- }
165
- } catch (error) {
166
- // 如果出现错误,打印错误信息
167
- console.error("Form submission error:", error);
168
- setSubmitStatus({
169
- success: false,
170
- message:
171
- error instanceof Error
172
- ? `Error: ${error.message}`
173
- : "Failed to submit the form. Please try again later.",
174
- });
175
- } finally {
176
- setSubmitting(false);
177
- }
178
- };
179
-
180
- return (
181
- <div
182
- ref={ref}
183
- className={clsx(
184
- "py-4 grid max-w-2xl grid-cols-1 gap-x-6 sm:grid-cols-2 w-full",
185
- className || "gap-y-6"
186
- )}
187
- {...rest}
188
- >
189
- {fields.map((field, index) => {
190
- const {
191
- controlName,
192
- feildStyle,
193
- isExtends,
194
- name,
195
- className,
196
- inputClassName,
197
- labelClassName,
198
- ...rest
199
- } = field;
200
-
201
- // FileUpload 组件特殊处理
202
- if (controlName === "FileUpload") {
203
- const FileUploadControl = getFileUpload(feildStyle);
204
- return (
205
- <FileUploadControl
206
- key={name || index}
207
- name={name || "attachments"}
208
- formSalt={formSalt}
209
- className={clsx(classNames?.inputContainer, className)}
210
- inputClassName={clsx(classNames?.input, inputClassName)}
211
- labelClassName={clsx(classNames?.label, labelClassName)}
212
- maxSize={3 * 1024 * 1024}
213
- onChange={handleFileChange}
214
- onUploadStateChange={handleFileUploadStateChange}
215
- placeholder="Click to select file"
216
- uploadingText="Uploading..."
217
- selectedText="Selected: {name}"
218
- accept="*/*"
219
- {...rest}
220
- />
221
- );
222
- }
223
-
224
- // 普通输入控件
225
- const Control = getControl(controlName, feildStyle);
226
- return (
227
- <Control
228
- key={name || index}
229
- name={name}
230
- className={clsx(classNames?.inputContainer, className)}
231
- labelClassName={clsx(classNames?.label, labelClassName)}
232
- inputClassName={clsx(classNames?.input, inputClassName)}
233
- requiredClassName={classNames?.required}
234
- value={formData[name as keyof QuoteRequest] as string}
235
- onChange={(e) => handleChange(e, isExtends)}
236
- error={errors[name as keyof FormErrors]}
237
- {...rest}
238
- />
239
- );
240
- })}
241
-
242
- {/* 蜜罐字段 - 对用户隐藏但对机器人可见 */}
243
- <div style={{ display: "none" }}>
244
- <input
245
- type="text"
246
- name="phone"
247
- value={formData.phone}
248
- onChange={handleChange}
249
- tabIndex={-1}
250
- autoComplete="off"
251
- />
252
- </div>
253
- <div
254
- className={clsx(
255
- "col-span-full flex items-center gap-x-6 px-0 py-2",
256
- submit?.containerClassName
257
- )}
258
- >
259
- {submitStatus.message && !submitStatus.success && (
260
- <div
261
- className={`text-sm ${
262
- submitStatus.success ? "text-green-600" : "text-red-600"
263
- }`}
264
- >
265
- {submitStatus.message}
266
- </div>
267
- )}
268
- <Submit
269
- className={clsx(
270
- "flex gap-2 items-center relative shadow-sm btn btn-lg nowrap",
271
- submit?.className || "btn-primary"
272
- )}
273
- title={submit?.title || "Send Message"}
274
- spinner={
275
- <div className="left-8 flex items-center justify-center">
276
- <div className="animate-spin rounded-full h-5 w-5 border-t-2 border-b-2 border-white" />
277
- </div>
278
- }
279
- rawHtml={submit?.rawHtml}
280
- needClasses={submit?.needClasses}
281
- submitting={submitting}
282
- disabled={submitting || isUploading}
283
- onClick={handleClick}
284
- />
285
- </div>
286
- </div>
287
- );
288
- }
289
- );
1
+ import { forwardRef, useState } from "react";
2
+ import { Submit } from "./Submit";
3
+ import { type UploadedFile } from "./FileUpload2";
4
+ import clsx from "clsx";
5
+ import { encrypt } from "./funcs";
6
+ import { ContactFormProps, QuoteRequest } from "./types";
7
+ import { getControl, getFileUpload } from "./factory";
8
+
9
+ interface FormErrors {
10
+ name?: string;
11
+ email?: string;
12
+ message?: string;
13
+ }
14
+
15
+ export const ContactForm = forwardRef<HTMLDivElement, ContactFormProps>(
16
+ (props, ref) => {
17
+ const {
18
+ submit,
19
+ actionUrl = "/api/ask-for-quote",
20
+ formSalt,
21
+ fields = [],
22
+ className,
23
+ classNames,
24
+ fromCta,
25
+ ...rest
26
+ } = props;
27
+ const [formData, setFormData] = useState<QuoteRequest>({
28
+ name: "",
29
+ email: "",
30
+ company: "",
31
+ message: "",
32
+ phone: "", // 初始化蜜罐字段
33
+ attachments: [], // 附件 ID 列表
34
+ });
35
+ // 错误状态
36
+ const [errors, setErrors] = useState<FormErrors>({});
37
+ const [submitting, setSubmitting] = useState(false);
38
+ const [isUploading, setIsUploading] = useState(false);
39
+ const [submitStatus, setSubmitStatus] = useState<{
40
+ success?: boolean;
41
+ message?: string;
42
+ }>({});
43
+
44
+ // 处理输入变化
45
+ const handleChange = (
46
+ e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
47
+ isExtends?: boolean
48
+ ) => {
49
+ const { name, value } = e.target;
50
+ if (isExtends) {
51
+ setFormData((prev) => ({
52
+ ...prev,
53
+ extents: {
54
+ ...(prev.extends || {}),
55
+ [name]: value,
56
+ },
57
+ }));
58
+ } else {
59
+ setFormData((prev) => ({
60
+ ...prev,
61
+ [name]: value,
62
+ }));
63
+ }
64
+
65
+ setSubmitStatus({}); // 重置提交状态
66
+
67
+ // 清除对应字段的错误
68
+ if (errors[name as keyof FormErrors]) {
69
+ setErrors((prev) => ({
70
+ ...prev,
71
+ [name]: undefined,
72
+ }));
73
+ }
74
+ };
75
+
76
+ // 处理文件上传变化(单文件)
77
+ const handleFileChange = (fileInfo: UploadedFile | null) => {
78
+ setFormData((prev) => ({
79
+ ...prev,
80
+ attachments: fileInfo ? [fileInfo] : [], // 存储完整的文件信息
81
+ }));
82
+ };
83
+
84
+ // 处理文件上传状态变化
85
+ const handleFileUploadStateChange = (uploading: boolean) => {
86
+ setIsUploading(uploading);
87
+ };
88
+ // 验证表单
89
+ const validateForm = (): boolean => {
90
+ const newErrors: FormErrors = {};
91
+
92
+ // if (!formData.name?.trim()) {
93
+ // newErrors.name = "Please enter your name";
94
+ // }
95
+
96
+ if (!formData.email?.trim()) {
97
+ newErrors.email = "Please enter your email";
98
+ } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
99
+ newErrors.email = "Please enter a valid email address";
100
+ }
101
+
102
+ if (!formData.message?.trim()) {
103
+ newErrors.message = "Please enter your message";
104
+ }
105
+
106
+ // 检查蜜罐字段 - 如果填写了则表示是机器人
107
+ if (formData.phone) {
108
+ // 悄悄失败,不显示错误信息
109
+ console.log("Honeypot triggered - likely spam submission");
110
+ return false;
111
+ }
112
+
113
+ setErrors(newErrors);
114
+ return Object.keys(newErrors).length === 0;
115
+ };
116
+
117
+ const handleClick = async (event: React.MouseEvent) => {
118
+ event.preventDefault(); // 阻止表单默认提交行为
119
+
120
+ // 验证表单
121
+ if (!validateForm()) {
122
+ return; // 如果验证失败,不继续提交
123
+ }
124
+
125
+ try {
126
+ setSubmitting(true);
127
+ setSubmitStatus({}); // 重置提交状态
128
+ const response = await fetch(actionUrl, {
129
+ method: "POST",
130
+ body: JSON.stringify({
131
+ ...formData,
132
+ //TODO:后面改成从window 或者传入
133
+ fromCta: fromCta || window?.lastCta,
134
+ encryptedField: encrypt(formData.phone, formSalt),
135
+ }),
136
+ headers: {
137
+ "X-Request-URL": window.location.href,
138
+ "Content-Type": "application/json",
139
+ },
140
+ });
141
+
142
+ if (!response.ok) {
143
+ throw new Error(`Server responded with status: ${response.status}`);
144
+ }
145
+
146
+ const result = await response.json();
147
+
148
+ setSubmitStatus({
149
+ success: result.success,
150
+ message: result.message,
151
+ });
152
+
153
+ if (result.success) {
154
+ // 重置表单
155
+ setFormData({
156
+ name: "",
157
+ email: "",
158
+ company: "",
159
+ message: "",
160
+ phone: "",
161
+ attachments: [],
162
+ });
163
+ window.location.href = "/thanks";
164
+ }
165
+ } catch (error) {
166
+ // 如果出现错误,打印错误信息
167
+ console.error("Form submission error:", error);
168
+ setSubmitStatus({
169
+ success: false,
170
+ message:
171
+ error instanceof Error
172
+ ? `Error: ${error.message}`
173
+ : "Failed to submit the form. Please try again later.",
174
+ });
175
+ } finally {
176
+ setSubmitting(false);
177
+ }
178
+ };
179
+
180
+ return (
181
+ <div
182
+ ref={ref}
183
+ className={clsx(
184
+ "py-4 grid max-w-2xl grid-cols-1 gap-x-6 sm:grid-cols-2 w-full",
185
+ className || "gap-y-6"
186
+ )}
187
+ {...rest}
188
+ >
189
+ {fields.map((field, index) => {
190
+ const {
191
+ controlName,
192
+ feildStyle,
193
+ isExtends,
194
+ name,
195
+ className,
196
+ inputClassName,
197
+ labelClassName,
198
+ ...rest
199
+ } = field;
200
+
201
+ // FileUpload 组件特殊处理
202
+ if (controlName === "FileUpload") {
203
+ const FileUploadControl = getFileUpload(feildStyle);
204
+ return (
205
+ <FileUploadControl
206
+ key={name || index}
207
+ name={name || "attachments"}
208
+ formSalt={formSalt}
209
+ className={clsx(classNames?.inputContainer, className)}
210
+ inputClassName={clsx(classNames?.input, inputClassName)}
211
+ labelClassName={clsx(classNames?.label, labelClassName)}
212
+ maxSize={3 * 1024 * 1024}
213
+ onChange={handleFileChange}
214
+ onUploadStateChange={handleFileUploadStateChange}
215
+ placeholder="Click to select file"
216
+ uploadingText="Uploading..."
217
+ selectedText="Selected: {name}"
218
+ accept="*/*"
219
+ {...rest}
220
+ />
221
+ );
222
+ }
223
+
224
+ // 普通输入控件
225
+ const Control = getControl(controlName, feildStyle);
226
+ return (
227
+ <Control
228
+ key={name || index}
229
+ name={name}
230
+ className={clsx(classNames?.inputContainer, className)}
231
+ labelClassName={clsx(classNames?.label, labelClassName)}
232
+ inputClassName={clsx(classNames?.input, inputClassName)}
233
+ requiredClassName={classNames?.required}
234
+ value={formData[name as keyof QuoteRequest] as string}
235
+ onChange={(e) => handleChange(e, isExtends)}
236
+ error={errors[name as keyof FormErrors]}
237
+ {...rest}
238
+ />
239
+ );
240
+ })}
241
+
242
+ {/* 蜜罐字段 - 对用户隐藏但对机器人可见 */}
243
+ <div style={{ display: "none" }}>
244
+ <input
245
+ type="text"
246
+ name="phone"
247
+ value={formData.phone}
248
+ onChange={handleChange}
249
+ tabIndex={-1}
250
+ autoComplete="off"
251
+ />
252
+ </div>
253
+ <div
254
+ className={clsx(
255
+ "col-span-full flex items-center gap-x-6 px-0 py-2",
256
+ submit?.containerClassName
257
+ )}
258
+ >
259
+ {submitStatus.message && !submitStatus.success && (
260
+ <div
261
+ className={`text-sm ${
262
+ submitStatus.success ? "text-green-600" : "text-red-600"
263
+ }`}
264
+ >
265
+ {submitStatus.message}
266
+ </div>
267
+ )}
268
+ <Submit
269
+ className={clsx(
270
+ "flex gap-2 items-center relative shadow-sm btn btn-lg nowrap",
271
+ submit?.className || "btn-primary"
272
+ )}
273
+ title={submit?.title || "Send Message"}
274
+ spinner={
275
+ <div className="left-8 flex items-center justify-center">
276
+ <div className="animate-spin rounded-full h-5 w-5 border-t-2 border-b-2 border-white" />
277
+ </div>
278
+ }
279
+ rawHtml={submit?.rawHtml}
280
+ needClasses={submit?.needClasses}
281
+ submitting={submitting}
282
+ disabled={submitting || isUploading}
283
+ onClick={handleClick}
284
+ />
285
+ </div>
286
+ </div>
287
+ );
288
+ }
289
+ );
@@ -1,48 +1,48 @@
1
- import { forwardRef } from "react";
2
- import { FieldConfig } from "./types";
3
-
4
- export type InputProps = Omit<FieldConfig, "feildStyle"> & {
5
- requiredClassName?: string;
6
- value?: string;
7
- onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
8
- error?: string;
9
- };
10
-
11
- export const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
12
- const {
13
- label,
14
- name,
15
- required,
16
- requiredClassName,
17
- labelClassName,
18
- inputClassName,
19
- type,
20
- autoFocus,
21
- value,
22
- onChange,
23
- error,
24
- ...rest
25
- } = props;
26
-
27
- return (
28
- <div {...rest}>
29
- <label htmlFor={name} className={labelClassName}>
30
- {label}
31
- {required ? <span className={requiredClassName}>*</span> : ""}
32
- </label>
33
- <input
34
- ref={ref}
35
- type={type || "text"}
36
- id={name}
37
- name={name}
38
- required={required}
39
- className={inputClassName}
40
- autoComplete={name}
41
- autoFocus={autoFocus}
42
- value={value}
43
- onChange={onChange}
44
- />
45
- {error && <p className="text-red-500 mt-1">{error}</p>}
46
- </div>
47
- );
48
- });
1
+ import { forwardRef } from "react";
2
+ import { FieldConfig } from "./types";
3
+
4
+ export type InputProps = Omit<FieldConfig, "feildStyle"> & {
5
+ requiredClassName?: string;
6
+ value?: string;
7
+ onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
8
+ error?: string;
9
+ };
10
+
11
+ export const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
12
+ const {
13
+ label,
14
+ name,
15
+ required,
16
+ requiredClassName,
17
+ labelClassName,
18
+ inputClassName,
19
+ type,
20
+ autoFocus,
21
+ value,
22
+ onChange,
23
+ error,
24
+ ...rest
25
+ } = props;
26
+
27
+ return (
28
+ <div {...rest}>
29
+ <label htmlFor={name} className={labelClassName}>
30
+ {label}
31
+ {required ? <span className={requiredClassName}>*</span> : ""}
32
+ </label>
33
+ <input
34
+ ref={ref}
35
+ type={type || "text"}
36
+ id={name}
37
+ name={name}
38
+ required={required}
39
+ className={inputClassName}
40
+ autoComplete={name}
41
+ autoFocus={autoFocus}
42
+ value={value}
43
+ onChange={onChange}
44
+ />
45
+ {error && <p className="text-red-500 mt-1">{error}</p>}
46
+ </div>
47
+ );
48
+ });