diff --git a/index.html b/index.html index 775bdd3..6175204 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - Vite + React + TS + PlayEdu
diff --git a/src/App.scss b/src/App.scss index b9d355d..25a8670 100644 --- a/src/App.scss +++ b/src/App.scss @@ -1,7 +1,6 @@ #root { - max-width: 1280px; + width: 100%; margin: 0 auto; - padding: 2rem; text-align: center; } diff --git a/src/assets/iconfont/iconfont.css b/src/assets/iconfont/iconfont.css new file mode 100644 index 0000000..ef30ad7 --- /dev/null +++ b/src/assets/iconfont/iconfont.css @@ -0,0 +1,83 @@ +@font-face { + font-family: "iconfont"; /* Project id 3943555 */ + src: url('iconfont.woff2?t=1679275935231') format('woff2'), + url('iconfont.woff?t=1679275935231') format('woff'), + url('iconfont.ttf?t=1679275935231') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-waterprint:before { + content: "\e747"; +} + +.icon-adduser:before { + content: "\e743"; +} + +.icon-upvideo:before { + content: "\e744"; +} + +.icon-onlinelesson:before { + content: "\e745"; +} + +.icon-department:before { + content: "\e746"; +} + +.icon-icon-drag:before { + content: "\e740"; +} + +.icon-icon-edit:before { + content: "\e741"; +} + +.icon-icon-delete:before { + content: "\e742"; +} + +.icon-icon-video:before { + content: "\e73f"; +} + +.icon-icon-home:before { + content: "\e737"; +} + +.icon-icon-category:before { + content: "\e738"; +} + +.icon-icon-file:before { + content: "\e739"; +} + +.icon-icon-study:before { + content: "\e73a"; +} + +.icon-icon-user:before { + content: "\e73b"; +} + +.icon-icon-setting:before { + content: "\e73c"; +} + +.icon-icon-password:before { + content: "\e73d"; +} + +.icon-a-icon-logout:before { + content: "\e73e"; +} + diff --git a/src/assets/iconfont/iconfont.ttf b/src/assets/iconfont/iconfont.ttf new file mode 100644 index 0000000..d3e0d7d Binary files /dev/null and b/src/assets/iconfont/iconfont.ttf differ diff --git a/src/assets/iconfont/iconfont.woff b/src/assets/iconfont/iconfont.woff new file mode 100644 index 0000000..aab9bdf Binary files /dev/null and b/src/assets/iconfont/iconfont.woff differ diff --git a/src/assets/iconfont/iconfont.woff2 b/src/assets/iconfont/iconfont.woff2 new file mode 100644 index 0000000..699058c Binary files /dev/null and b/src/assets/iconfont/iconfont.woff2 differ diff --git a/src/assets/images/commen/avatar.png b/src/assets/images/commen/avatar.png new file mode 100644 index 0000000..b71724a Binary files /dev/null and b/src/assets/images/commen/avatar.png differ diff --git a/src/assets/images/login/banner.png b/src/assets/images/login/banner.png new file mode 100644 index 0000000..5c4ef98 Binary files /dev/null and b/src/assets/images/login/banner.png differ diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000..6906040 Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/compenents/footer/index.module.scss b/src/compenents/footer/index.module.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/compenents/footer/index.tsx b/src/compenents/footer/index.tsx new file mode 100644 index 0000000..9b82fee --- /dev/null +++ b/src/compenents/footer/index.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import { Layout } from "antd"; + +export const Footer: React.FC = () => { + return ( + + + + ); +}; diff --git a/src/compenents/index.ts b/src/compenents/index.ts new file mode 100644 index 0000000..877f643 --- /dev/null +++ b/src/compenents/index.ts @@ -0,0 +1,2 @@ +export * from "./footer"; +export * from "./no-header"; \ No newline at end of file diff --git a/src/compenents/no-header/index.module.scss b/src/compenents/no-header/index.module.scss new file mode 100644 index 0000000..7520dd2 --- /dev/null +++ b/src/compenents/no-header/index.module.scss @@ -0,0 +1,29 @@ +.app-header { + background-color: #f6f6f6; + box-sizing: border-box; + -moz-box-sizing: border-box; + /* Firefox */ + -webkit-box-sizing: border-box; + /* Safari */ + padding: 0px 24px; +} + +.main-header { + width: 1200px; + height: 60px; + line-height: 60px; + display: flex; + align-items: center; + justify-content: space-between; + margin: 0 auto; +} + +.App-logo { + width: 124px; + height: 40px; +} + +.top-main { + display: flex; + align-items: center; +} diff --git a/src/compenents/no-header/index.tsx b/src/compenents/no-header/index.tsx new file mode 100644 index 0000000..6fd1f50 --- /dev/null +++ b/src/compenents/no-header/index.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import styles from "./index.module.scss"; +import { useDispatch } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { Image } from "antd"; +import logo from "../../assets/logo.png"; + +export const NoHeader: React.FC = () => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + + return ( +
+
+ +
+
+ ); +}; diff --git a/src/index.scss b/src/index.scss index 2c3fac6..38e2d6b 100644 --- a/src/index.scss +++ b/src/index.scss @@ -1,3 +1,7 @@ +@import "./assets/iconfont/iconfont.css"; + +$primaryColor: #ff4d4f; + :root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; @@ -67,3 +71,184 @@ button:focus-visible { background-color: #f9f9f9; } } + +.w-250px { + width: 250px; +} + +.w-300px { + width: 300px; +} + +.w-350px { + width: 350px; +} + +.w-400px { + width: 200px; +} + +.w-450px { + width: 200px; +} + +.w-500px { + width: 200px; +} + +.mr-5 { + margin-right: 5px; +} + +.ml-5 { + margin-left: 5px; +} + +.ml-8 { + margin-left: 8px; +} + +.mt-10 { + margin-top: 10px; +} + +.ml-15 { + margin-left: 15px; +} + +.ml-120 { + margin-left: 120px; +} + +.ml-16 { + margin-left: 16px; +} + +.mr-16 { + margin-right: 16px; +} + +.mb-8 { + margin-bottom: 8px; +} + +.mb-10 { + margin-bottom: 10px; +} + +.mt-24 { + margin-top: 24px; +} + +.mb-24 { + margin-bottom: 24px; +} + +.mr-24 { + margin-right: 24px; +} + +.mb-28 { + margin-bottom: 28px; +} + +.mt-50 { + margin-top: 50px; +} + +.mb-50 { + margin-bottom: 50px; +} + +.helper-text { + height: 24px; + font-size: 12px; + font-weight: 400; + color: rgba(0, 0, 0, 0.45); + line-height: 24px; +} + +.float-left { + width: 100%; + height: auto; + float: left; +} + +.d-flex { + display: flex; + align-items: center; +} + +.j-flex { + display: flex; + justify-content: center; +} + +.d-j-flex { + display: flex; + align-items: center; + justify-content: center; +} + +.j-r-flex { + display: flex; + justify-content: right; +} + +.j-b-flex { + display: flex; + align-items: center; + justify-content: space-between; +} + +.c-flex { + display: flex; + flex-direction: column; +} + +.c-a-flex { + display: flex; + flex-direction: column; + align-items: center; +} + +.flex-1 { + flex: 1; +} + +.primary { + color: $primaryColor; +} + +.c-yellow { + color: #e1a500; +} + +.c-success { + color: #04c877; +} + +.c-green { + color: #00cc66; +} + +.c-red { + color: $primaryColor; +} + +.login-box { + .ant-btn { + font-size: 18px !important; + font-weight: 500 !important; + } + + .ant-input, + .ant-input-password { + font-weight: 400; + font-size: 18px !important; + background-color: #f6f6f6; + &::placeholder { + color: rgba(0, 0, 0, 0.45); + } + } +} diff --git a/src/pages/login/index.module.scss b/src/pages/login/index.module.scss index e69de29..7da7581 100644 --- a/src/pages/login/index.module.scss +++ b/src/pages/login/index.module.scss @@ -0,0 +1,73 @@ +.login-content { + width: 100%; + float: left; + height: 100vh; + background-color: #fff; + position: relative; + display: flex; + flex-direction: column; + justify-content: space-between; + .title { + width: 120px; + height: auto; + font-size: 30px; + font-weight: 600; + color: #333333; + line-height: 30px; + border-bottom: 4px solid #ff4d4f; + box-sizing: border-box; + padding-bottom: 10px; + margin: 0 auto; + margin-top: 100px; + } + .login-box { + width: 1200px; + height: 366px; + background: #ffffff; + display: flex; + margin: 0 auto; + margin-top: 80px; + .left-box { + width: 595px; + height: 100%; + box-sizing: border-box; + padding: 33px 60px; + .icon { + width: 475px; + height: 300px; + } + } + .right-box { + width: 520px; + height: 100%; + box-sizing: border-box; + border-left: 1px solid #d8d8d8; + padding: 0px 60px; + display: flex; + flex-direction: column; + .captcha-box { + width: 125px; + height: 54px; + margin-left: 15px; + border-radius: 8px; + background-color: rgba(#ff4d4f, 0.1); + display: flex; + + .catpcha-loading-box { + width: 125px; + height: 54px; + line-height: 54px; + text-align: center; + } + + .captcha { + width: 125px; + height: 54px; + border: none; + cursor: pointer; + border-radius: 8px; + } + } + } + } +} diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index efffc50..c9d5698 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -1,40 +1,166 @@ -import { Button } from "antd"; +import { Spin, Input, Button, message } from "antd"; +import React, { useState, useEffect } from "react"; +import styles from "./index.module.scss"; +import banner from "../../assets/images/login/banner.png"; import { useDispatch, useSelector } from "react-redux"; +import { useNavigate } from "react-router-dom"; import { loginAction, logoutAction } from "../../store/user/loginUserSlice"; +import { login, system, user } from "../../api/index"; +import { setToken } from "../../utils/index"; +import { Footer, NoHeader } from "../../compenents"; -const LoginPage = () => { +const LoginPage: React.FC = () => { const dispatch = useDispatch(); - const loginState = useSelector((state: any) => { - return state.loginUser.value; - }); - - return ( - <> - + const navigate = useNavigate(); + const [loading, setLoading] = useState(false); + const [image, setImage] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [captchaVal, setCaptchaVal] = useState(""); + const [captchaKey, setCaptchaKey] = useState(""); + const [captchaLoading, setCaptchaLoading] = useState(true); - {loginState.isLogin && ( - - )} - + useEffect(() => { + fetchImageCaptcha(); + }, []); + + const fetchImageCaptcha = () => { + setCaptchaLoading(true); + system.imageCaptcha().then((res: any) => { + setImage(res.data.image); + setCaptchaKey(res.data.key); + setCaptchaLoading(false); + }); + }; + + const loginSubmit = (e: any) => { + if (!email) { + message.error("请输入学员邮箱账号"); + return; + } + if (!password) { + message.error("请输入密码"); + return; + } + if (!captchaVal) { + message.error("请输入图形验证码"); + return; + } + if (loading) { + return; + } + handleSubmit(); + }; + + const keyUp = (e: any) => { + if (e.keyCode === 13) { + loginSubmit(e); + } + }; + + const handleSubmit = () => { + if (loading) { + return; + } + setLoading(true); + login + .login(email, password, captchaKey, captchaVal) + .then((res: any) => { + const token = res.data.token; + setToken(token); + getUser(); + }) + .catch((e) => { + setLoading(false); + setCaptchaVal(""); + fetchImageCaptcha(); + }); + }; + + const getUser = () => { + user.detail().then((res: any) => { + const data = res.data; + dispatch(logoutAction(data.user)); + setLoading(false); + navigate("/"); + }); + }; + + return ( +
+
+ +
学员登录
+
+
+ +
+
+
+ { + setEmail(e.target.value); + }} + style={{ width: 400, height: 54 }} + placeholder="请输入学员邮箱账号" + onKeyUp={(e) => keyUp(e)} + /> +
+
+ { + setPassword(e.target.value); + }} + style={{ width: 400, height: 54 }} + placeholder="请输入密码" + /> +
+
+ { + setCaptchaVal(e.target.value); + }} + onKeyUp={(e) => keyUp(e)} + /> +
+ {captchaLoading && ( +
+ +
+ )} + + {!captchaLoading && ( + + )} +
+
+
+ +
+
+
+
+
+ +
+
); }; -export default LoginPage; \ No newline at end of file +export default LoginPage;