diff --git a/5.react/8.ecommerce/ecommerce-front/package.json b/5.react/8.ecommerce/ecommerce-front/package.json
index 381722a..60da907 100644
--- a/5.react/8.ecommerce/ecommerce-front/package.json
+++ b/5.react/8.ecommerce/ecommerce-front/package.json
@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "@ant-design/icons": "^5.3.6",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
diff --git a/5.react/8.ecommerce/ecommerce-front/pnpm-lock.yaml b/5.react/8.ecommerce/ecommerce-front/pnpm-lock.yaml
index a33e855..61faef4 100644
--- a/5.react/8.ecommerce/ecommerce-front/pnpm-lock.yaml
+++ b/5.react/8.ecommerce/ecommerce-front/pnpm-lock.yaml
@@ -5,6 +5,9 @@ settings:
excludeLinksFromLockfile: false
dependencies:
+ '@ant-design/icons':
+ specifier: ^5.3.6
+ version: 5.3.6(react-dom@18.2.0)(react@18.2.0)
'@testing-library/jest-dom':
specifier: ^5.17.0
version: 5.17.0
diff --git a/5.react/8.ecommerce/ecommerce-front/src/components/admin/AddCategory.tsx b/5.react/8.ecommerce/ecommerce-front/src/components/admin/AddCategory.tsx
new file mode 100644
index 0000000..360fafe
--- /dev/null
+++ b/5.react/8.ecommerce/ecommerce-front/src/components/admin/AddCategory.tsx
@@ -0,0 +1,55 @@
+import { Button, Form, Input, message } from "antd";
+import React, { useState } from "react";
+import Layout from "../core/Layout";
+import axios from "axios";
+import { API } from "../../config";
+import { isAuth } from "../../helpers/auth";
+import { Link } from "react-router-dom";
+import { Jwt } from "../../types/auth";
+
+const AddCategory = () => {
+ const { user, token } = isAuth() as Jwt;
+
+ async function addCategory(name: string) {
+ try {
+ let response = await axios.post<{ name: string }>(
+ `${API}/category/create/${user._id}`,
+ {
+ name: name,
+ },
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ }
+ );
+ message.success(`[${response.data.name}] 分类添加成功`);
+ } catch (error: any) {
+ message.error(error.response.data.error);
+ }
+ }
+
+ const onFinish = (value: { name: string }) => {
+ const { name } = value;
+ if (name) addCategory(name);
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default AddCategory;
diff --git a/5.react/8.ecommerce/ecommerce-front/src/components/admin/AddProduct.tsx b/5.react/8.ecommerce/ecommerce-front/src/components/admin/AddProduct.tsx
new file mode 100644
index 0000000..8076eae
--- /dev/null
+++ b/5.react/8.ecommerce/ecommerce-front/src/components/admin/AddProduct.tsx
@@ -0,0 +1,113 @@
+import { Button, Form, Input, message, Select, Upload } from "antd";
+import { UploadOutlined } from "@ant-design/icons";
+import React, { useEffect, useState } from "react";
+import { useShallow } from "zustand/react/shallow";
+import Layout from "../core/Layout";
+// import { useDispatch, useSelector } from "react-redux"
+// import { getCategory } from "../../store/actions/category.actions"
+// import { AppState } from "../../store/reducers/index"
+// import { CategoryState } from "../../store/reducers/category.reducer"
+import { RcFile } from "antd/lib/upload";
+import axios from "axios";
+import { API } from "../../config";
+import { isAuth } from "../../helpers/auth";
+import { Jwt } from "../../types/auth";
+import { useCategoryStore } from "../../store/category";
+
+const AddProduct = () => {
+ const [file, setFile] = useState();
+
+ const getCategories = useCategoryStore((state) => state.getCategories);
+ const categories = useCategoryStore((state) => state.result);
+
+ useEffect(() => {
+ getCategories();
+ }, [getCategories]);
+
+ const { user, token } = isAuth() as Jwt;
+
+ const onFinish = (product: any) => {
+ const formData = new FormData();
+ for (let attr in product) {
+ formData.set(attr, product[attr]);
+ }
+ if (typeof file !== "undefined") {
+ formData.set("photo", file);
+ }
+
+ axios
+ .post(`${API}/product/create/${user._id}`, formData, {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ })
+ .then(
+ () => {
+ message.success("商品添加成功");
+ },
+ () => {
+ message.error("商品添加失败");
+ }
+ );
+ };
+
+ const addProductForm = () => {
+ const props = {
+ accept: "image/*",
+ beforeUpload: function (file: RcFile) {
+ setFile(file);
+ return false;
+ },
+ };
+ return (
+
+
+ }>上传商品封面
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ };
+
+ return (
+
+ {addProductForm()}
+
+ );
+};
+
+export default AddProduct;
diff --git a/5.react/8.ecommerce/ecommerce-front/src/components/admin/AdminDashboard.tsx b/5.react/8.ecommerce/ecommerce-front/src/components/admin/AdminDashboard.tsx
new file mode 100644
index 0000000..267d927
--- /dev/null
+++ b/5.react/8.ecommerce/ecommerce-front/src/components/admin/AdminDashboard.tsx
@@ -0,0 +1,69 @@
+import React, { useState } from "react";
+import Layout from "../core/Layout";
+import { Jwt } from "../../types/auth";
+import { isAuth } from "../../helpers/auth";
+import { Col, Descriptions, Menu, MenuProps, Row, Typography } from "antd";
+import { Link, useNavigate } from "react-router-dom";
+import {
+ ShoppingCartOutlined,
+ UserOutlined,
+ OrderedListOutlined,
+} from "@ant-design/icons";
+
+const items: MenuProps["items"] = [
+ {
+ label: "添加分类",
+ key: "/create/category",
+ icon: ,
+ },
+ {
+ label: "添加产品",
+ key: "/create/product",
+ icon: ,
+ },
+ {
+ label: "订单列表",
+ key: "/admin/orders",
+ icon: ,
+ },
+];
+const AdminDashboard = () => {
+ const {
+ user: { name, email },
+ } = isAuth() as Jwt;
+
+ const navigate = useNavigate();
+
+ const [current, setCurrent] = useState("");
+ const onClick: MenuProps["onClick"] = (e) => {
+ setCurrent(e.key);
+ navigate(e.key);
+ };
+
+ const adminLinks = () => (
+ <>
+ 管理员链接
+
+ >
+ );
+
+ const adminInfo = () => (
+
+ {name}
+ {email}
+ 管理员
+
+ );
+
+ return (
+
+
+ {adminLinks()}
+
+ {adminInfo()}
+
+
+ );
+};
+
+export default AdminDashboard;
diff --git a/5.react/8.ecommerce/ecommerce-front/src/components/admin/AdminRoute.tsx b/5.react/8.ecommerce/ecommerce-front/src/components/admin/AdminRoute.tsx
new file mode 100644
index 0000000..7c6c1c4
--- /dev/null
+++ b/5.react/8.ecommerce/ecommerce-front/src/components/admin/AdminRoute.tsx
@@ -0,0 +1,18 @@
+import React, { FC } from "react";
+import { Navigate } from "react-router-dom";
+import { isAuth } from "../../helpers/auth";
+import { Jwt } from "../../types/auth";
+
+const AdminRoute: FC = ({ component: Component, ...rest }) => {
+ const auth = isAuth();
+ if (auth) {
+ const {
+ user: { role },
+ } = auth as Jwt;
+ if (role === 1) return ;
+ }
+
+ return ;
+};
+
+export default AdminRoute;
diff --git a/5.react/8.ecommerce/ecommerce-front/src/components/admin/Dashboard.tsx b/5.react/8.ecommerce/ecommerce-front/src/components/admin/Dashboard.tsx
new file mode 100644
index 0000000..41ed0b8
--- /dev/null
+++ b/5.react/8.ecommerce/ecommerce-front/src/components/admin/Dashboard.tsx
@@ -0,0 +1,12 @@
+import React from "react"
+import Layout from "../core/Layout"
+
+const Dashboard = () => {
+ return (
+
+ Dashboard
+
+ )
+}
+
+export default Dashboard
diff --git a/5.react/8.ecommerce/ecommerce-front/src/components/admin/PrivateRoute.tsx b/5.react/8.ecommerce/ecommerce-front/src/components/admin/PrivateRoute.tsx
new file mode 100644
index 0000000..b540109
--- /dev/null
+++ b/5.react/8.ecommerce/ecommerce-front/src/components/admin/PrivateRoute.tsx
@@ -0,0 +1,14 @@
+import React, { FC } from "react";
+import { Navigate } from "react-router-dom";
+import { isAuth } from "../../helpers/auth";
+
+const PrivateRoute: FC = ({ component: Component, ...rest }) => {
+ const auth = isAuth();
+ if (auth) {
+ return ;
+ }
+
+ return ;
+};
+
+export default PrivateRoute;
diff --git a/5.react/8.ecommerce/ecommerce-front/src/components/core/SignIn.tsx b/5.react/8.ecommerce/ecommerce-front/src/components/core/SignIn.tsx
index c13c29c..6e7967c 100644
--- a/5.react/8.ecommerce/ecommerce-front/src/components/core/SignIn.tsx
+++ b/5.react/8.ecommerce/ecommerce-front/src/components/core/SignIn.tsx
@@ -1,15 +1,13 @@
-import React, { useEffect } from "react";
+import React from "react";
import Layout from "./Layout";
import { Button, Form, Input, Result } from "antd";
import { SigninPayload, useSigninStore } from "../../store/auth";
import { isAuth } from "../../helpers/auth";
import { Jwt } from "../../types/auth";
-import { redirect, useNavigate } from "react-router-dom";
+import { Navigate } from "react-router-dom";
const SignIn = () => {
const signinState = useSigninStore();
- const navigate = useNavigate();
- const auth = isAuth();
// 2. 登录失败 显示错误信息
const showError = () => {
if (signinState.loaded && !signinState.success) {
@@ -23,21 +21,23 @@ const SignIn = () => {
}
};
- useEffect(() => {
- // 3. 登录成功 根据角色跳转到对应的管理页面
+ // 3. 登录成功 根据角色跳转到对应的管理页面
+ const redirectToDashboard = () => {
+ const auth = isAuth();
if (auth) {
const {
user: { role },
} = auth as Jwt;
+
if (role === 0) {
// 注册用户
- navigate("/user/dashboard");
+ return ;
} else {
// 管理员
- navigate("/admin/dashboard");
+ return ;
}
}
- });
+ };
const onFinish = async (value: SigninPayload) => {
// 发送登录请求
await signinState.signin(value);
@@ -58,9 +58,10 @@ const SignIn = () => {
);
- return auth ? null : (
+ return (
{showError()}
+ {redirectToDashboard()}
{signinForm()}
);
diff --git a/5.react/8.ecommerce/ecommerce-front/src/router/index.tsx b/5.react/8.ecommerce/ecommerce-front/src/router/index.tsx
index 0f05c17..5b18f88 100644
--- a/5.react/8.ecommerce/ecommerce-front/src/router/index.tsx
+++ b/5.react/8.ecommerce/ecommerce-front/src/router/index.tsx
@@ -3,6 +3,12 @@ import Home from "../components/core/Home";
import Shop from "../components/core/Shop";
import SignIn from "../components/core/SignIn";
import SignUp from "../components/core/SignUp";
+import Dashboard from "../components/admin/Dashboard";
+import PrivateRoute from "../components/admin/PrivateRoute";
+import AdminRoute from "../components/admin/AdminRoute";
+import AdminDashboard from "../components/admin/AdminDashboard";
+import AddCategory from "../components/admin/AddCategory";
+import AddProduct from "../components/admin/AddProduct";
const router = createBrowserRouter([
{
@@ -21,6 +27,22 @@ const router = createBrowserRouter([
path: "signup",
element: ,
},
+ {
+ path: "user/dashboard",
+ element: ,
+ },
+ {
+ path: "admin/dashboard",
+ element: ,
+ },
+ {
+ path: "/create/category",
+ element: ,
+ },
+ {
+ path: "/create/product",
+ element: ,
+ }
]);
export default router;
diff --git a/5.react/8.ecommerce/ecommerce-front/src/store/category.ts b/5.react/8.ecommerce/ecommerce-front/src/store/category.ts
new file mode 100644
index 0000000..65beb88
--- /dev/null
+++ b/5.react/8.ecommerce/ecommerce-front/src/store/category.ts
@@ -0,0 +1,37 @@
+import { create } from "zustand";
+import { API } from "../config";
+import axios from "axios";
+
+export interface Category {
+ _id: string;
+ name: string;
+}
+export interface CategoryState {
+ loaded: boolean;
+ success: boolean;
+ result: Category[];
+ getCategories: () => void;
+}
+
+const useCategoryStore = create((set, get) => ({
+ loaded: false,
+ success: false,
+ result: [],
+ getCategories: async () => {
+ set({ loaded: false, success: false, result: [] });
+ let isSuccess = false;
+ // 请求接口
+
+ try {
+ let response = await axios.get(`${API}/categories`);
+ set({ loaded: true, success: true, result: response.data });
+
+ isSuccess = true;
+ } catch (error: any) {
+ isSuccess = false;
+ }
+ return isSuccess;
+ },
+}));
+
+export { useCategoryStore };