SignUp.tsx 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. import { useRef, useState } from "react";
  2. import { useIntl } from "react-intl";
  3. import { Alert, Button, message } from "antd";
  4. import type { ProFormInstance } from "@ant-design/pro-components";
  5. import {
  6. CheckCard,
  7. ProForm,
  8. ProFormCaptcha,
  9. ProFormCheckbox,
  10. ProFormText,
  11. StepsForm,
  12. } from "@ant-design/pro-components";
  13. import { MailOutlined, LockOutlined } from "@ant-design/icons";
  14. import { get, post } from "../../request";
  15. import type {
  16. IEmailCertificationResponse,
  17. IInviteData,
  18. IInviteRequest,
  19. IInviteResponse,
  20. } from "../../api/Auth";
  21. import { dashboardBasePath } from "../../utils";
  22. import { get as getUiLang } from "../../locales";
  23. import {
  24. AccountInfo,
  25. type IAccountForm,
  26. onSignIn,
  27. SignUpSuccess,
  28. } from "../nut/users/SignUp";
  29. interface IFormData {
  30. email: string;
  31. lang: string;
  32. }
  33. const SingUpWidget = () => {
  34. const intl = useIntl();
  35. const formRef = useRef<ProFormInstance | undefined>(undefined);
  36. const [error, setError] = useState<string>();
  37. const [agree, setAgree] = useState(false);
  38. const [invite, setInvite] = useState<IInviteData>();
  39. return (
  40. <StepsForm<IFormData>
  41. formRef={formRef}
  42. onFinish={async (_values: IFormData) => {}}
  43. formProps={{
  44. validateMessages: {
  45. required: "此项为必填项",
  46. },
  47. }}
  48. submitter={{
  49. render(props, dom) {
  50. if (props.step === 0) {
  51. return (
  52. <Button
  53. type="primary"
  54. disabled={!agree}
  55. onClick={() => props.onSubmit?.()}
  56. >
  57. {"下一步"}
  58. </Button>
  59. );
  60. } else if (props.step === 3) {
  61. return <></>;
  62. } else {
  63. return dom;
  64. }
  65. },
  66. }}
  67. >
  68. <StepsForm.StepForm<{
  69. name: string;
  70. }>
  71. name="welcome"
  72. title={intl.formatMessage({ id: "labels.sign-up" })}
  73. stepProps={{
  74. description: "注册wikipali基础版",
  75. }}
  76. onFinish={async () => {
  77. return true;
  78. }}
  79. >
  80. <Alert
  81. message={"wikipali的阅读,字典,搜索功能无需注册就能使用。"}
  82. style={{ marginBottom: 8 }}
  83. />
  84. <CheckCard.Group
  85. onChange={(value) => {
  86. console.log("value", value);
  87. }}
  88. defaultValue="A"
  89. style={{ width: "100%" }}
  90. size="small"
  91. >
  92. <CheckCard
  93. title={intl.formatMessage({ id: "labels.software.edition.guest" })}
  94. description={
  95. <div>
  96. <div>✅经文阅读</div>
  97. <div>✅字典</div>
  98. <div>✅经文搜索</div>
  99. <div>❌翻译</div>
  100. <div>❌参加课程</div>
  101. </div>
  102. }
  103. value="B"
  104. disabled
  105. />
  106. <CheckCard
  107. title={intl.formatMessage({ id: "labels.software.edition.basic" })}
  108. description={
  109. <div>
  110. <div>✅逐词解析</div>
  111. <div>✅翻译</div>
  112. <div>✅参加课程</div>
  113. <div>❌公开发布译文和逐词解析</div>
  114. <div>❌公开发布用户字典和术语</div>
  115. <div>❌建立课程</div>
  116. <div>❌建立群组</div>
  117. </div>
  118. }
  119. value="A"
  120. />
  121. <CheckCard
  122. title={intl.formatMessage({ id: "labels.software.edition.pro" })}
  123. disabled
  124. description={
  125. <div>
  126. <div>✅逐词解析</div>
  127. <div>✅翻译</div>
  128. <div>✅参加课程</div>
  129. <div>✅公开发布译文和逐词解析</div>
  130. <div>✅公开发布用户字典和术语</div>
  131. <div>✅建立课程</div>
  132. <div>✅建立群组</div>
  133. </div>
  134. }
  135. value="C"
  136. />
  137. </CheckCard.Group>
  138. <ProFormCheckbox.Group
  139. name="checkbox"
  140. layout="horizontal"
  141. options={["我已经了解基础版的功能限制"]}
  142. fieldProps={{
  143. onChange(checkedValue) {
  144. if (checkedValue.length > 0) {
  145. setAgree(true);
  146. } else {
  147. setAgree(false);
  148. }
  149. },
  150. }}
  151. />
  152. </StepsForm.StepForm>
  153. <StepsForm.StepForm<{
  154. email: string;
  155. captcha: number;
  156. }>
  157. name="checkbox"
  158. title={intl.formatMessage({ id: "auth.sign-up.email-certification" })}
  159. stepProps={{
  160. description: " ",
  161. }}
  162. onFinish={async (value) => {
  163. if (!invite) {
  164. message.error("无效的id");
  165. return false;
  166. }
  167. const url = `/v2/email-certification/${invite?.id}`;
  168. console.info("api request email-certification", url);
  169. try {
  170. const res = await get<IEmailCertificationResponse>(url);
  171. console.debug("api response", res);
  172. if (res.ok) {
  173. if (res.data === value.captcha) {
  174. message.success(intl.formatMessage({ id: "flashes.success" }));
  175. } else {
  176. setError("验证码不正确");
  177. }
  178. //建立账号
  179. } else {
  180. setError(intl.formatMessage({ id: `error.${res.message}` }));
  181. }
  182. return res.ok;
  183. } catch (error) {
  184. setError(error as string);
  185. return false;
  186. }
  187. }}
  188. >
  189. {error ? <Alert type="error" message={error} /> : undefined}
  190. <ProForm.Group>
  191. <ProFormText
  192. fieldProps={{
  193. size: "large",
  194. prefix: <MailOutlined />,
  195. }}
  196. name="email"
  197. required
  198. placeholder={intl.formatMessage({ id: "forms.fields.email.label" })}
  199. rules={[
  200. {
  201. required: true,
  202. type: "email",
  203. },
  204. ]}
  205. />
  206. </ProForm.Group>
  207. <ProForm.Group>
  208. <ProFormCaptcha
  209. fieldProps={{
  210. size: "large",
  211. prefix: <LockOutlined />,
  212. }}
  213. captchaProps={{
  214. size: "large",
  215. }}
  216. placeholder={"请输入验证码"}
  217. captchaTextRender={(timing, count) => {
  218. if (timing) {
  219. return `${count} ${"获取验证码"}`;
  220. }
  221. return "获取验证码";
  222. }}
  223. name="captcha"
  224. rules={[
  225. {
  226. required: true,
  227. message: "请输入验证码!",
  228. },
  229. ]}
  230. onGetCaptcha={async () => {
  231. const values = formRef.current?.getFieldsValue();
  232. const url = `/v2/email-certification`;
  233. const data: IInviteRequest = {
  234. email: values.email,
  235. lang: getUiLang(),
  236. subject: intl.formatMessage({
  237. id: "labels.email.sign-up.subject",
  238. }),
  239. studio: "",
  240. dashboard: dashboardBasePath(),
  241. };
  242. console.info("api request", values);
  243. try {
  244. const res = await post<IInviteRequest, IInviteResponse>(
  245. url,
  246. data
  247. );
  248. console.debug("api response", res);
  249. if (res.ok) {
  250. setInvite(res.data);
  251. message.success(
  252. "邮件发送成功,请登录此邮箱查收邮件,并将邮件中的验证码填入。"
  253. );
  254. } else {
  255. setError(intl.formatMessage({ id: `error.${res.message}` }));
  256. message.error("邮件发送失败");
  257. }
  258. } catch (error) {
  259. setError(error as string);
  260. message.error("邮件发送失败");
  261. }
  262. }}
  263. />
  264. </ProForm.Group>
  265. </StepsForm.StepForm>
  266. <StepsForm.StepForm<IAccountForm>
  267. name="info"
  268. title={intl.formatMessage({ id: "auth.sign-up.info" })}
  269. onFinish={async (values: IAccountForm) => {
  270. if (typeof invite === "undefined") {
  271. return false;
  272. }
  273. values.email = invite.email;
  274. const signUp = await onSignIn(invite.id, values);
  275. if (signUp) {
  276. if (signUp.ok) {
  277. return true;
  278. } else {
  279. message.error(signUp.message);
  280. return false;
  281. }
  282. } else {
  283. return false;
  284. }
  285. }}
  286. request={async () => {
  287. console.debug("account info", invite);
  288. return {
  289. id: invite ? invite.id : "",
  290. username: "",
  291. nickname: "",
  292. password: "",
  293. password2: "",
  294. email: invite ? invite.email : "",
  295. lang: "zh-Hant",
  296. };
  297. }}
  298. >
  299. <AccountInfo email={false} />
  300. </StepsForm.StepForm>
  301. <StepsForm.StepForm
  302. name="finish"
  303. title={intl.formatMessage({ id: "labels.done" })}
  304. >
  305. <SignUpSuccess />
  306. </StepsForm.StepForm>
  307. </StepsForm>
  308. );
  309. };
  310. export default SingUpWidget;