Skip to content

Commit

Permalink
feat:category
Browse files Browse the repository at this point in the history
  • Loading branch information
laoriy committed May 2, 2024
1 parent eceda78 commit 4600674
Show file tree
Hide file tree
Showing 11 changed files with 355 additions and 10 deletions.
1 change: 1 addition & 0 deletions 5.react/8.ecommerce/ecommerce-front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 3 additions & 0 deletions 5.react/8.ecommerce/ecommerce-front/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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 (
<Layout title="添加分类" subTitle="">
<Form onFinish={onFinish}>
<Form.Item name="name" label="分类名称">
<Input />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
添加分类
</Button>
</Form.Item>
</Form>
<Button>
<Link to="/admin/dashboard">返回 Dashboard</Link>
</Button>
</Layout>
);
};

export default AddCategory;
Original file line number Diff line number Diff line change
@@ -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<RcFile>();

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 (
<Form onFinish={onFinish} initialValues={{ category: "" }}>
<Form.Item>
<Upload {...props}>
<Button icon={<UploadOutlined />}>上传商品封面</Button>
</Upload>
</Form.Item>
<Form.Item name="name" label="商品名称">
<Input />
</Form.Item>
<Form.Item name="description" label="商品描述">
<Input />
</Form.Item>
<Form.Item name="price" label="商品价格">
<Input />
</Form.Item>
<Form.Item name="category" label="所属分类">
<Select>
<Select.Option value="">请选择分类</Select.Option>
{categories.map(item => (
<Select.Option key={item._id} value={item._id}>
{item.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item name="quantity" label="商品数量">
<Input />
</Form.Item>
<Form.Item name="shipping" label="是否需要运输">
<Select>
<Select.Option value="1"></Select.Option>
<Select.Option value="0"></Select.Option>
</Select>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
添加商品
</Button>
</Form.Item>
</Form>
);
};

return (
<Layout title="添加商品" subTitle="">
{addProductForm()}
</Layout>
);
};

export default AddProduct;
Original file line number Diff line number Diff line change
@@ -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: <ShoppingCartOutlined />,
},
{
label: "添加产品",
key: "/create/product",
icon: <UserOutlined />,
},
{
label: "订单列表",
key: "/admin/orders",
icon: <OrderedListOutlined />,
},
];
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 = () => (
<>
<Typography.Title level={5}>管理员链接</Typography.Title>
<Menu items={items} onClick={onClick} selectedKeys={[current]}></Menu>
</>
);

const adminInfo = () => (
<Descriptions title="管理员信息" bordered>
<Descriptions.Item label="昵称">{name}</Descriptions.Item>
<Descriptions.Item label="邮件">{email}</Descriptions.Item>
<Descriptions.Item label="角色">管理员</Descriptions.Item>
</Descriptions>
);

return (
<Layout title="管理员 Dashboard" subTitle="">
<Row>
<Col span="4">{adminLinks()}</Col>
<Col span="1"></Col>
<Col span="19">{adminInfo()}</Col>
</Row>
</Layout>
);
};

export default AdminDashboard;
Original file line number Diff line number Diff line change
@@ -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<any> = ({ component: Component, ...rest }) => {
const auth = isAuth();
if (auth) {
const {
user: { role },
} = auth as Jwt;
if (role === 1) return <Component {...rest} />;
}

return <Navigate replace to="/signin" />;
};

export default AdminRoute;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react"
import Layout from "../core/Layout"

const Dashboard = () => {
return (
<Layout title="用户 Dashboard" subTitle="">
Dashboard
</Layout>
)
}

export default Dashboard
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React, { FC } from "react";
import { Navigate } from "react-router-dom";
import { isAuth } from "../../helpers/auth";

const PrivateRoute: FC<any> = ({ component: Component, ...rest }) => {
const auth = isAuth();
if (auth) {
return <Component {...rest} />;
}

return <Navigate replace to="/signin" />;
};

export default PrivateRoute;
21 changes: 11 additions & 10 deletions 5.react/8.ecommerce/ecommerce-front/src/components/core/SignIn.tsx
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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 <Navigate replace to="/user/dashboard" />;
} else {
// 管理员
navigate("/admin/dashboard");
return <Navigate replace to="/admin/dashboard" />;
}
}
});
};
const onFinish = async (value: SigninPayload) => {
// 发送登录请求
await signinState.signin(value);
Expand All @@ -58,9 +58,10 @@ const SignIn = () => {
</Form>
);

return auth ? null : (
return (
<Layout title="登录" subTitle="嘿, 小伙伴, 立即登录到拉钩电商系统吧">
{showError()}
{redirectToDashboard()}
{signinForm()}
</Layout>
);
Expand Down
22 changes: 22 additions & 0 deletions 5.react/8.ecommerce/ecommerce-front/src/router/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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([
{
Expand All @@ -21,6 +27,22 @@ const router = createBrowserRouter([
path: "signup",
element: <SignUp />,
},
{
path: "user/dashboard",
element: <PrivateRoute component={Dashboard} />,
},
{
path: "admin/dashboard",
element: <AdminRoute component={AdminDashboard} />,
},
{
path: "/create/category",
element: <AdminRoute component={AddCategory} />,
},
{
path: "/create/product",
element: <AdminRoute component={AddProduct} />,
}
]);

export default router;
Loading

0 comments on commit 4600674

Please sign in to comment.