Forráskód Böngészése

:construction: add react form & intl demo

Jeremy Zheng 3 éve
szülő
commit
57f77f547e
33 módosított fájl, 289 hozzáadás és 14 törlés
  1. 20 4
      dashboard/src/App.tsx
  2. 0 0
      dashboard/src/components/nut/users/ChangePassword.tsx
  3. 18 0
      dashboard/src/components/nut/users/NonSignInSharedLinks.tsx
  4. 0 0
      dashboard/src/components/nut/users/Profile.tsx
  5. 42 0
      dashboard/src/components/nut/users/SignIn.tsx
  6. 1 0
      dashboard/src/layouts/anonymous/index.tsx
  7. 3 0
      dashboard/src/locales/en-US/buttons.ts
  8. 3 0
      dashboard/src/locales/en-US/forms.ts
  9. 3 0
      dashboard/src/locales/en-US/index.ts
  10. 3 0
      dashboard/src/locales/en-US/nut/index.ts
  11. 68 0
      dashboard/src/locales/index.ts
  12. 11 0
      dashboard/src/locales/languages.ts
  13. 5 0
      dashboard/src/locales/zh-Hans/buttons.ts
  14. 6 0
      dashboard/src/locales/zh-Hans/forms.ts
  15. 14 0
      dashboard/src/locales/zh-Hans/index.ts
  16. 6 0
      dashboard/src/locales/zh-Hans/nut/index.ts
  17. 3 0
      dashboard/src/locales/zh-Hans/tables.ts
  18. 3 0
      dashboard/src/locales/zh-Hant/buttons.ts
  19. 3 0
      dashboard/src/locales/zh-Hant/forms.ts
  20. 3 0
      dashboard/src/locales/zh-Hant/index.ts
  21. 3 0
      dashboard/src/locales/zh-Hant/nut/index.ts
  22. 2 2
      dashboard/src/pages/nut/users/account-info.tsx
  23. 10 1
      dashboard/src/pages/nut/users/sign-in.tsx
  24. 9 1
      dashboard/src/pages/nut/users/sign-up.tsx
  25. 5 5
      documents/development/frontend/README.md
  26. 1 1
      documents/development/frontend/react-day-1/README.md
  27. 44 0
      documents/development/frontend/react-day-2/README.md
  28. BIN
      documents/development/frontend/react-day-2/form.png
  29. BIN
      documents/development/frontend/react-day-2/intl/by-component.png
  30. BIN
      documents/development/frontend/react-day-2/intl/by-function.png
  31. BIN
      documents/development/frontend/react-day-2/intl/import.png
  32. BIN
      documents/development/frontend/react-day-2/intl/kv.png
  33. BIN
      documents/development/frontend/react-day-2/intl/project.png

+ 20 - 4
dashboard/src/App.tsx

@@ -1,15 +1,31 @@
-import React from "react";
 import { BrowserRouter } from "react-router-dom";
+import { ConfigProvider } from "antd";
+import { IntlProvider } from "react-intl";
 
 import Router from "./Router";
+import locales, {
+  get as getLocale,
+  DEFAULT as DEFAULT_LOCALE,
+} from "./locales";
 
 import "./App.css";
 
+const lang = getLocale();
+const i18n = locales(lang);
+
 function Widget() {
   return (
-    <BrowserRouter basename={process.env.PUBLIC_URL}>
-      <Router />
-    </BrowserRouter>
+    <IntlProvider
+      messages={i18n.messages}
+      locale={lang}
+      defaultLocale={DEFAULT_LOCALE}
+    >
+      <ConfigProvider locale={i18n.antd}>
+        <BrowserRouter basename={process.env.PUBLIC_URL}>
+          <Router />
+        </BrowserRouter>
+      </ConfigProvider>
+    </IntlProvider>
   );
 }
 

+ 0 - 0
dashboard/src/components/nut/users/change-password.tsx → dashboard/src/components/nut/users/ChangePassword.tsx


+ 18 - 0
dashboard/src/components/nut/users/NonSignInSharedLinks.tsx

@@ -0,0 +1,18 @@
+import { FormattedMessage } from "react-intl";
+import { Link } from "react-router-dom";
+import { Space } from "antd";
+
+const Widget = () => {
+  return (
+    <Space>
+      <Link to="/anonymous/users/sign-in">
+        <FormattedMessage id="nut.users.sign-in.title" />
+      </Link>
+      <Link to="/anonymous/users/sign-up">
+        <FormattedMessage id="nut.users.sign-up.title" />
+      </Link>
+    </Space>
+  );
+};
+
+export default Widget;

+ 0 - 0
dashboard/src/components/nut/users/profile.tsx → dashboard/src/components/nut/users/Profile.tsx


+ 42 - 0
dashboard/src/components/nut/users/SignIn.tsx

@@ -0,0 +1,42 @@
+import { useIntl } from "react-intl";
+import { ProForm, ProFormText } from "@ant-design/pro-components";
+import { message } from "antd";
+
+interface IFormData {
+  email: string;
+  password: string;
+}
+const Widget = () => {
+  const intl = useIntl();
+
+  return (
+    <ProForm<IFormData>
+      onFinish={async (values: IFormData) => {
+        // TODO
+        console.log(values);
+        message.success(intl.formatMessage({ id: "flashes.success" }));
+      }}
+    >
+      <ProForm.Group>
+        <ProFormText
+          width="md"
+          name="email"
+          required
+          label={intl.formatMessage({ id: "forms.fields.email.label" })}
+          rules={[{ required: true, type: "email", max: 255, min: 6 }]}
+        />
+      </ProForm.Group>
+      <ProForm.Group>
+        <ProFormText
+          width="md"
+          name="password"
+          required
+          label={intl.formatMessage({ id: "forms.fields.password.label" })}
+          rules={[{ required: true, max: 32, min: 8 }]}
+        />
+      </ProForm.Group>
+    </ProForm>
+  );
+};
+
+export default Widget;

+ 1 - 0
dashboard/src/layouts/anonymous/index.tsx

@@ -7,6 +7,7 @@ const Widget = () => {
       <div>anonymous layout header</div>
       <div>
         <Outlet />
+
         <div>
           <Space>
             <Link to="/anonymous/users/sign-in">Sign in</Link>

+ 3 - 0
dashboard/src/locales/en-US/buttons.ts

@@ -0,0 +1,3 @@
+const items = {};
+
+export default items;

+ 3 - 0
dashboard/src/locales/en-US/forms.ts

@@ -0,0 +1,3 @@
+const items = {};
+
+export default items;

+ 3 - 0
dashboard/src/locales/en-US/index.ts

@@ -0,0 +1,3 @@
+const items = {};
+
+export default items;

+ 3 - 0
dashboard/src/locales/en-US/nut/index.ts

@@ -0,0 +1,3 @@
+const items = {};
+
+export default items;

+ 68 - 0
dashboard/src/locales/index.ts

@@ -0,0 +1,68 @@
+import Cookies from "js-cookie";
+import type { Locale as AntdLocale } from "antd/lib/locale-provider";
+import antdEnUS from "antd/lib/locale/en_US";
+import antdZhCN from "antd/lib/locale/zh_CN";
+import antdZhTW from "antd/lib/locale/zh_TW";
+import "moment/locale/zh-cn";
+import "moment/locale/zh-tw";
+import "moment/locale/es";
+import "moment/locale/fr";
+import "moment/locale/ja";
+import "moment/locale/ko";
+
+import languages from "./languages";
+import enUS from "./en-US";
+import zhHans from "./zh-Hans";
+import zhHant from "./zh-Hant";
+
+const KEY = "locale";
+
+export const DEFAULT: string =
+  process.env.REACT_APP_DEFAULT_LOCALE || "zh-Hans";
+export const LANGUAGES: string[] = process.env.REACT_APP_LANGUAGES?.split(
+  ","
+) || ["en-US", "zh-Hans"];
+
+export const get = (): string => {
+  return localStorage.getItem(KEY) || Cookies.get(KEY) || DEFAULT;
+};
+
+export const set = (lang: string, reload: boolean) => {
+  Cookies.set(KEY, lang);
+  localStorage.setItem(KEY, lang);
+  if (reload) {
+    window.location.reload();
+  }
+};
+
+export const remove = () => {
+  Cookies.remove(KEY);
+  localStorage.removeItem(KEY);
+};
+
+interface ILocale {
+  antd: AntdLocale;
+  messages: Record<string, string>;
+}
+
+const messages = (lang: string): ILocale => {
+  switch (lang) {
+    case "en-US":
+      return {
+        messages: { ...enUS, ...languages },
+        antd: antdEnUS,
+      };
+    case "zh-Hant":
+      return {
+        messages: { ...zhHant, ...languages },
+        antd: antdZhTW,
+      };
+    default:
+      return {
+        messages: { ...zhHans, ...languages },
+        antd: antdZhCN,
+      };
+  }
+};
+
+export default messages;

+ 11 - 0
dashboard/src/locales/languages.ts

@@ -0,0 +1,11 @@
+const items = {
+  "languages.en-US": "English",
+  "languages.zh-Hans": "简体中文",
+  "languages.zh-Hant": "繁體中文",
+  "languages.fr": "Français",
+  "languages.es": "Español",
+  "languages.ja": "日本語",
+  "languages.ko": "한국어",
+};
+
+export default items;

+ 5 - 0
dashboard/src/locales/zh-Hans/buttons.ts

@@ -0,0 +1,5 @@
+const items = {
+  "buttons.submit": "提交",
+};
+
+export default items;

+ 6 - 0
dashboard/src/locales/zh-Hans/forms.ts

@@ -0,0 +1,6 @@
+const items = {
+  "forms.fields.email.label": "电子邮箱",
+  "forms.fields.password.label": "密码",
+};
+
+export default items;

+ 14 - 0
dashboard/src/locales/zh-Hans/index.ts

@@ -0,0 +1,14 @@
+import forms from "./forms";
+import buttons from "./buttons";
+import tables from "./tables";
+import nut from "./nut";
+
+const items = {
+  "flashes.success": "操作成功",
+  ...buttons,
+  ...forms,
+  ...tables,
+  ...nut,
+};
+
+export default items;

+ 6 - 0
dashboard/src/locales/zh-Hans/nut/index.ts

@@ -0,0 +1,6 @@
+const items = {
+  "nut.users.sign-in.title": "欢迎登录",
+  "nut.users.sign-up.title": "新用户注册",
+};
+
+export default items;

+ 3 - 0
dashboard/src/locales/zh-Hans/tables.ts

@@ -0,0 +1,3 @@
+const items = {};
+
+export default items;

+ 3 - 0
dashboard/src/locales/zh-Hant/buttons.ts

@@ -0,0 +1,3 @@
+const items = {};
+
+export default items;

+ 3 - 0
dashboard/src/locales/zh-Hant/forms.ts

@@ -0,0 +1,3 @@
+const items = {};
+
+export default items;

+ 3 - 0
dashboard/src/locales/zh-Hant/index.ts

@@ -0,0 +1,3 @@
+const items = {};
+
+export default items;

+ 3 - 0
dashboard/src/locales/zh-Hant/nut/index.ts

@@ -0,0 +1,3 @@
+const items = {};
+
+export default items;

+ 2 - 2
dashboard/src/pages/nut/users/account-info.tsx

@@ -1,5 +1,5 @@
-import ChangePassword from "../../../components/nut/users/change-password";
-import Profile from "../../../components/nut/users/profile";
+import ChangePassword from "../../../components/nut/users/ChangePassword";
+import Profile from "../../../components/nut/users/Profile";
 
 const Widget = () => {
   return (

+ 10 - 1
dashboard/src/pages/nut/users/sign-in.tsx

@@ -1,5 +1,14 @@
+import SignInForm from "../../../components/nut/users/SignIn";
+import SharedLinks from "../../../components/nut/users/NonSignInSharedLinks";
+
 const Widget = () => {
-  return <div>sign in</div>;
+  return (
+    <div>
+      <SignInForm />
+      <br />
+      <SharedLinks />
+    </div>
+  );
 };
 
 export default Widget;

+ 9 - 1
dashboard/src/pages/nut/users/sign-up.tsx

@@ -1,5 +1,13 @@
+import SharedLinks from "../../../components/nut/users/NonSignInSharedLinks";
+
 const Widget = () => {
-  return <div>sign up</div>;
+  return (
+    <div>
+      sign up
+      <br />
+      <SharedLinks />
+    </div>
+  );
 };
 
 export default Widget;

+ 5 - 5
documents/development/frontend/README.md

@@ -37,8 +37,8 @@ yarn start
 
 ## React
 
-- [React 第一天](react-day-1/)
-- [React 第二天](react-day-2/)
-- [React 第三天](react-day-3/)
-- [React 第四天](react-day-4/)
-- [React 第五天](react-day-5/)
+- [React 第一天: router & layout](react-day-1/)
+- [React 第二天: i18n & form](react-day-2/)
+- [React 第三天: table](react-day-3/)
+- [React 第四天: http request](react-day-4/)
+- [React 第五天: page](react-day-5/)

+ 1 - 1
documents/development/frontend/react-day-1/README.md

@@ -47,7 +47,7 @@
 ## 作业
 
 - 每人把自己负责的子项目的所有 pages, components 规划一下 并写出 demo(`<div> bla </div>`即可)
-  - 参见`pages/nut/users/sign-in.tsx`创建`PROJECT_ID/blaA/blaB.tsx`页面 对应页面路径`/PROJECT_ID/blaA/blaB`
+  - 参见`pages/nut/users/sign-in.tsx`创建`PROJECT_ID/blaA/blaB.tsx`页面 对应页面路径`/my/PROJECT_ID/blaA/blaB`
   - 参见`components/nut/users/change-password.tsx`创建`PROJECT_ID/blaX/blaY.tsx`组件
   - 参见`Routes.tsx` 绑定路径和页面
 - 需要从 http url 中取参数的页面,要写好 params 并验证

+ 44 - 0
documents/development/frontend/react-day-2/README.md

@@ -0,0 +1,44 @@
+# i18n & form
+
+## i18n
+
+### 添加翻译条目
+
+- 按照**project id**分门别类
+
+  ![project](intl/project.png)
+
+- 内容就是简单的 KV
+
+  ![kv](intl/kv.png)
+
+- project id 引入方法参见
+
+  ![import](intl/import.png)
+
+### 使用方法
+
+- 组件式
+
+  ![by component](intl/by-component.png)
+
+- 函数式
+
+  ![by function](intl/by-function.png)
+
+## 表单例子
+
+![form](form.png)
+
+## 参考文档
+
+- [ProForm](https://procomponents.ant.design/components/form/)
+
+## 练习内容
+
+- 表单
+  - 从简单到复杂
+  - 不考虑表单布局 全部用默认样式
+  - 随便找个自己的页面 表单放进去 能够显示出来即可
+  - 所有自定义的字符串都要放入国际化目录(比如中文在:locales/zh-Hans)
+  - onFinish 里打印出表单值即可,标记为 todo

BIN
documents/development/frontend/react-day-2/form.png


BIN
documents/development/frontend/react-day-2/intl/by-component.png


BIN
documents/development/frontend/react-day-2/intl/by-function.png


BIN
documents/development/frontend/react-day-2/intl/import.png


BIN
documents/development/frontend/react-day-2/intl/kv.png


BIN
documents/development/frontend/react-day-2/intl/project.png