提交 4575bf6d 作者: 翁进城

分类管理

上级 1e1ed168
......@@ -18,6 +18,7 @@
"antd": "^5.4.2",
"axios": "^1.4.0",
"dayjs": "^1.11.7",
"events": "^3.3.0",
"file-saver": "^2.0.5",
"js-base64": "^3.7.3",
"js-cookie": "^3.0.1",
......@@ -33,6 +34,7 @@
"react-quill": "^2.0.0",
"react-redux": "^8.0.5",
"react-router-dom": "^6.10.0",
"react-viewer": "^3.2.2",
"sort-by": "^1.2.0",
"xlsx": "^0.18.5"
},
......
......@@ -5,6 +5,7 @@ import { ActivityManageAPI } from './modules/activityManage';
import { CouponManageAPI } from './modules/couponManage';
import { CommonAPI } from './modules/common';
import { MakeManageAPI } from './modules/makeManage';
import { CategoryManageAPI } from './modules/categoryManage';
export {
PointManageAPI,
......@@ -14,4 +15,5 @@ export {
CommonAPI,
CouponManageAPI,
MakeManageAPI,
CategoryManageAPI
};
// 分类详情
export interface GoodsInfo {
goodsId: number;
goodsImg: string;
goodsName: string;
shelfStatus: number;
showCode: number;
}
// 分类列表
export interface categoryEntity {
goodsMasterType: string;
icon: string;
remark: string;
desc: string;
createTime: string;
goodsMasterTypeId: number | string;
goodsSlaveTypeDTO: Partial<categoryEntity>[];
pid: number | string;
isADD?: boolean;
}
// 分类目录
export interface categoryDec {
defaultType: number;
id: number;
sortName: string;
}
import axios from "../request";
import { PaginationEntity } from '~/common/interface/PaginationEntity';
export class CategoryManageAPI {
// 分类管理
// 分类目录
static directoryList = () => {
return axios.get("uavgoods/directory/directoryList");
};
// 新增或编辑目录
static addOrEditDirectory = (
data: { id?: number; directoryName: string }[]
) => {
return axios.post("uavgoods/directory/addOrEditDirectory", data);
};
// 删除目录
static removeDirectory = (id: number) => {
return axios.get("uavgoods/directory/removeDirectory", { params: { id } });
};
// 分类新增
static addClassification = (data: object): any => {
return axios.post("uavgoods/mgoods/addClassification", data);
};
// 分类列表
static getListGoodsTypeList = (type: number): any => {
return axios.get(`uavgoods/mgoods/getGoodsTypeInfoList/${type}`);
};
// PC端-根据分类信息新增自定义规格-下拉选项 (只查产品类型)
static getProductTypeInfoList = (): any => {
return axios.get("uavgoods/mgoods/getGoodsTypeInfoList");
};
// 所有分类信息
static getGoodsTypeListByOneLevel = (): any => {
return axios.get("uavgoods/mgoods/listGoodsTypeListByOneLevel");
};
// 分类修改
static updateClassification = (data: object): any => {
return axios.post("uavgoods/mgoods/updateClassification", data);
};
// 删除分类
static deleteGoodsTypeByOneLevel = (params: object): any => {
return axios.get("uavgoods/mgoods/deleteGoodsTypeByOneLevel", { params });
};
// 删除子分类
static deleteGoodsTypeByChildren = (params: object): any => {
return axios.get("uavgoods/mgoods/deleteGoodsTypeByChildren", { params });
};
// 查询二级分类
static listGoodsTypeListByTwoLevel = (params: object): any => {
return axios.get("uavgoods/mgoods/listGoodsTypeListByTwoLevel", { params });
};
// 查询其它服务
static listOtherService = (): any => {
return axios.get("uavgoods/mgoods/listOtherService");
};
// 分类上下移动
static exchangeSortType = (params: object): any => {
return axios.get("uavgoods/mgoods/exchangeSortType", { params });
};
// 分类详情
static getGoodsTypeDetail = (
obj: { id: number } & Pick<PaginationEntity, "pageNo" | "pageSize">
) => {
return axios.get("uavgoods/mgoods/getGoodsTypeDetail", { params: obj });
};
// 分类详情-安全编码开发修改
static updateIsShowCode = (
params: Pick<GoodsInfo, "goodsId" | "showCode">
) => {
return axios.get("uavgoods/appgoods/updateIsShowCode", { params });
};
// 分类详情-商品排序
static exchangeGoodsInfo = (firstId: number, secondId: number) => {
return axios.get("uavgoods/mgoods/exchangeGoodsInfo", {
params: { firstId, secondId },
});
};
// 分类详情-图片上传
static cateGoryDetailUpload = (data: FormData) => {
return axios.post("ossservlet/upload/imgOss", data);
};
// 行业分享码
static getAppletQRCode(params: { page: string; scene: string }) {
return axios.get("malluser/wechat/getAppletQRCode", { params });
}
}
import Viewer from "react-viewer";
import React from "react";
interface propsType {
visible: boolean;
currentImgList: any;
activeViewerIndex: number;
setVisible: Function;
}
const Index: React.FC<propsType> = ({
visible,
currentImgList,
activeViewerIndex,
setVisible,
}) => {
return (
<Viewer
visible={visible}
onClose={() => {
setVisible(false);
}}
images={currentImgList}
activeIndex={activeViewerIndex}
zIndex={1999}
/>
);
};
export default Index;
import { EventEmitter } from "events";
class QcEventEmitter extends EventEmitter {}
export default new QcEventEmitter();
import { Form, Input, InputNumber } from "antd";
interface Item {
key: string;
name: string;
detail: string;
startTime: string;
}
interface EditableCellProps extends React.HTMLAttributes<HTMLElement> {
editing: boolean;
dataIndex: string;
title: any;
inputType: "textarea" | "text";
record: Item;
index: number;
children: React.ReactNode;
}
const EditableCell: React.FC<EditableCellProps> = ({
editing,
dataIndex,
title,
inputType,
record,
index,
children,
...restProps
}) => {
const inputNode =
dataIndex === "desc" ? (
<Input.TextArea
maxLength={70}
showCount
rows={5}
placeholder={`请输入${title}`}
/>
) : (
<Input placeholder={`请输入${title}`} maxLength={15} />
);
return (
<td {...restProps}>
{editing ? (
<Form.Item
name={dataIndex}
style={{ margin: 0 }}
rules={[
{
required: dataIndex !== "desc",
message: `请输入${title}`,
},
]}
>
{inputNode}
</Form.Item>
) : (
children
)}
</td>
);
};
export default EditableCell;
.img-wrap{
.img-content{
width: 50%;
height: 50%;
position: relative;
.img{
width: 100%;
width: 100%;
}
.delete-img{
position: absolute;
top: 0;
right: 0;
transform: translate(25%,-25%);
width: 20px;
height: 20px;
}
}
}
\ No newline at end of file
import React, { useEffect, useImperativeHandle, useState } from "react";
import { Modal, Form, Input, Row, Col, Upload, message, Button } from "antd";
import {
LoadingOutlined,
PlusOutlined,
UploadOutlined,
} from "@ant-design/icons";
import Cookies from "js-cookie";
import { uploadImgURL } from "~/api/request";
import events from "~/events";
import deletePng from "~/assets/image/delete.png";
import "./index.scss";
function Index(props: any) {
const headers: any = {
token: Cookies.get("SXTB-TOKEN"),
};
const [form] = Form.useForm();
const [imageUrl, setImageUrl] = useState("");
const [fileList, setFileList]: any = useState([]);
useEffect(() => {
events.addListener("removeFileList", () => {
// console.log("执行了");
setFileList([]);
setImageUrl("");
});
events.addListener("setImgFile", (str) => {
setImageUrl(str);
form.setFieldsValue({ icon: str });
});
});
useImperativeHandle(props.baseRef, () => ({
getForm: () => form,
}));
function onFinish() {}
function onFinishFailed() {}
function getBase64(img: any, callback: Function) {
const reader = new FileReader();
reader.addEventListener("load", () => callback(reader.result));
reader.readAsDataURL(img);
}
function handleChange(val: any) {
// console.log("上传图片-->", val);
if (!["image/jpg", "image/jpeg", "image/png"].includes(val.file.type)) {
message.error("请上传图片格式为jpg,jpeg,png的图片");
form.setFieldsValue({ icon: "" });
setFileList([]);
return;
}
const limitM = 2;
const isLimit = val.file.size / 1024 / 1024 <= limitM;
if (!isLimit) {
message.error("图片最大上传2M");
form.setFieldsValue({ icon: "" });
setFileList([]);
return;
}
if (val.file.status == "error") {
message.error("服务器异常");
setFileList([]);
form.setFieldsValue({ icon: "" });
} else if (val.file.response && val.file.response.code != "200") {
message.error(val.file.response.message);
setFileList([]);
form.setFieldsValue({ icon: "" });
} else if (val.file.status === "done") {
setImageUrl(val.file.response.result[0]);
setFileList(val.fileList);
form.setFieldsValue({ icon: val.file.response.result[0] });
} else {
setFileList(val.fileList);
}
}
const beforeUpload = (file: any) => {
// const isPNG = file.type === 'image/png';
// if (!isPNG) {
// message.error(`${file.name}`);
// }
// return isPNG || Upload.LIST_IGNORE;
};
// 图片删除
const deleteImg = () => {
setImageUrl("");
form.setFieldsValue({ icon: undefined });
setFileList([]);
};
return (
<Modal
title={props.title}
visible={props.isVisable}
onOk={() => props.handleOk()}
onCancel={() => props.handleCancel()}
>
<Form
name="basic"
labelCol={{ span: 4 }}
wrapperCol={{ span: 16 }}
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
form={form}
>
<Form.Item
label="分类名称"
name="groupName"
rules={[{ required: true, message: "请输入分类名称" }]}
>
<Input placeholder="请输入分类名称" maxLength={15} allowClear />
</Form.Item>
<Form.Item
label="分类描述"
name="description"
rules={[{ required: false, message: "请输入分类描述" }]}
>
<Input.TextArea
placeholder="请输入分类描述"
showCount
rows={3}
maxLength={70}
allowClear
/>
</Form.Item>
<Form.Item
label="分类图标"
name="icon"
rules={[{ required: true, message: "请上传分类图标" }]}
>
{imageUrl ? (
<div className="img-wrap">
<div className="img-content">
<img
src={imageUrl}
className="img"
onClick={() => props.imgClick(imageUrl)}
/>
<img
src={deletePng}
alt=""
className="delete-img"
onClick={deleteImg}
/>
</div>
</div>
) : (
<Upload
// className="avatar-uploader"
name="uploadFile"
fileList={fileList}
action={uploadImgURL}
maxCount={1}
beforeUpload={beforeUpload}
onChange={handleChange}
headers={headers}
accept="image/*"
>
{fileList.length >= 1 ? (
""
) : (
<Button icon={<UploadOutlined />}>上传</Button>
)}
</Upload>
)}
</Form.Item>
<Form.Item
label="备注"
name="remark"
rules={[{ required: false, message: "请输入备注" }]}
>
<Input.TextArea rows={5} maxLength={150} showCount />
</Form.Item>
</Form>
</Modal>
);
}
export default Index;
import { Modal, Form, Input, Button, Row, Col, message } from "antd";
import React, { useEffect, useState } from "react";
import { PlusOutlined, MinusOutlined } from "@ant-design/icons";
import { PropsType } from "~/common/interface/modal";
import { categoryDec } from "~/api/modules/goods";
import { CategoryManageAPI } from "~/api";
interface selfPropsType {
directoryList: categoryDec[];
refreshDec: Function;
}
const AddOrEditDec: React.FC<PropsType & selfPropsType> = ({
isModalVisible,
handleOk,
handleCancel,
directoryList,
refreshDec,
}) => {
const [form] = Form.useForm<any>();
// 表单目录标题列表
const [addOrEditDirectoryList, setAddOrEditDirectoryList] = useState<
categoryDec[]
>([]);
// 是否点击删除按钮
const [isClickDle, setIsClickDle] = useState<boolean>(false);
useEffect(() => {
if (directoryList.length != 0 && !isClickDle) {
setAddOrEditDirectoryList(directoryList);
const defaultFormValue = directoryList.reduce(
(pre: any, cur: categoryDec) => {
Object.keys(cur).map((item: string) => {
if (item === "id") {
pre[cur[item]] = cur.sortName;
}
});
return pre;
},
{}
);
form.setFieldsValue(defaultFormValue);
}
}, [directoryList]);
// 新增或修改目录
const addDirectoryTitle = () => {
const decList: categoryDec[] = [...addOrEditDirectoryList].sort(
(a: categoryDec, b: categoryDec) => a.id - b.id
);
setAddOrEditDirectoryList([
...addOrEditDirectoryList,
{
id: decList[decList.length - 1].id + 1,
defaultType: 1,
sortName: "",
},
]);
};
// 删除目录
const deleteDirectory = async (id: number) => {
const bol: boolean = directoryList.some(
(item: categoryDec) => item.id === id
);
const index = addOrEditDirectoryList.findIndex(
(item: categoryDec) => item.id === id
);
if (bol) {
const res: any = await CategoryManageAPI.removeDirectory(id);
if (res.code === "200") {
message.success("删除成功");
setIsClickDle(true);
refreshDec(id);
} else {
return message.warning(res.message);
}
}
const obj: any = {};
obj[id] = undefined;
form.setFieldsValue(obj);
addOrEditDirectoryList.splice(index, 1);
setAddOrEditDirectoryList([...addOrEditDirectoryList]);
};
const directorySureEvent = () => {
form.validateFields().then(async (value: any) => {
const requestList = Object.keys(value).reduce((pre: any, cur: string) => {
const bol: boolean = directoryList.some(
(item: any) => item.id === Number(cur)
);
if (bol) {
pre.push({
id: Number(cur),
directoryName: value[cur],
});
} else {
pre.push({
directoryName: value[cur],
});
}
return pre;
}, []);
const res: any = await CategoryManageAPI.addOrEditDirectory(requestList);
if (res.code === "200") {
message.success("操作成功");
form.resetFields();
setIsClickDle(false);
handleOk();
} else {
message.warning(res.message);
}
});
};
const directoryCancel = () => {
form.resetFields();
setIsClickDle(false);
handleCancel();
};
return (
<Modal
title="目录管理"
visible={isModalVisible}
onOk={directorySureEvent}
onCancel={directoryCancel}
>
<Form form={form} labelCol={{ span: 6 }} wrapperCol={{ span: 16 }}>
<Form.Item
label="添加目录"
labelCol={{ span: 4 }}
wrapperCol={{ span: 16 }}
>
<Button icon={<PlusOutlined />} onClick={addDirectoryTitle} />
</Form.Item>
{addOrEditDirectoryList.map((item: categoryDec) => (
<Row key={item.id} gutter={{ xs: 8, sm: 16, md: 24 }}>
<Col span={16}>
<Form.Item
label="目录名称"
name={item.id}
rules={[{ required: true, message: "请输入目录名称" }]}
>
<Input placeholder="请输入目录名称" maxLength={30} />
</Form.Item>
</Col>
<Col>
{item.defaultType ? (
<Button
icon={<MinusOutlined />}
type="primary"
onClick={() => deleteDirectory(item.id)}
/>
) : (
""
)}
</Col>
</Row>
))}
</Form>
</Modal>
);
};
export default AddOrEditDec;
import { Modal, Form, Input, Button, Row, Col, message } from "antd";
import React, { useEffect, useState } from "react";
import { PlusOutlined, MinusOutlined } from "@ant-design/icons";
import { PropsType } from "~/common/interface/modal";
import { categoryDec } from "~/api/modules/goods";
import { CategoryManageAPI } from "~/api";
interface selfPropsType {
directoryList: categoryDec[];
refreshDec: Function;
}
const AddOrEditDec: React.FC<PropsType & selfPropsType> = ({
isModalVisible,
handleOk,
handleCancel,
directoryList,
refreshDec,
}) => {
const [form] = Form.useForm<any>();
// 表单目录标题列表
const [addOrEditDirectoryList, setAddOrEditDirectoryList] = useState<
categoryDec[]
>([]);
// 是否点击删除按钮
const [isClickDle, setIsClickDle] = useState<boolean>(false);
useEffect(() => {
if (directoryList.length != 0 && !isClickDle) {
setAddOrEditDirectoryList(directoryList);
const defaultFormValue = directoryList.reduce(
(pre: any, cur: categoryDec) => {
Object.keys(cur).map((item: string) => {
if (item === "id") {
pre[cur[item]] = cur.sortName;
}
});
return pre;
},
{}
);
form.setFieldsValue(defaultFormValue);
}
}, [directoryList]);
// 新增或修改目录
const addDirectoryTitle = () => {
const decList: categoryDec[] = [...addOrEditDirectoryList].sort(
(a: categoryDec, b: categoryDec) => a.id - b.id
);
setAddOrEditDirectoryList([
...addOrEditDirectoryList,
{
id: decList[decList.length - 1].id + 1,
defaultType: 1,
sortName: "",
},
]);
};
// 删除目录
const deleteDirectory = async (id: number) => {
const bol: boolean = directoryList.some(
(item: categoryDec) => item.id === id
);
const index = addOrEditDirectoryList.findIndex(
(item: categoryDec) => item.id === id
);
if (bol) {
const res: any = await CategoryManageAPI.removeDirectory(id);
if (res.code === "200") {
message.success("删除成功");
setIsClickDle(true);
refreshDec(id);
} else {
return message.warning(res.message);
}
}
const obj: any = {};
obj[id] = undefined;
form.setFieldsValue(obj);
addOrEditDirectoryList.splice(index, 1);
setAddOrEditDirectoryList([...addOrEditDirectoryList]);
};
const directorySureEvent = () => {
form.validateFields().then(async (value: any) => {
const requestList = Object.keys(value).reduce((pre: any, cur: string) => {
const bol: boolean = directoryList.some(
(item: any) => item.id === Number(cur)
);
if (bol) {
pre.push({
id: Number(cur),
directoryName: value[cur],
});
} else {
pre.push({
directoryName: value[cur],
});
}
return pre;
}, []);
const res: any = await CategoryManageAPI.addOrEditDirectory(requestList);
if (res.code === "200") {
message.success("操作成功");
form.resetFields();
setIsClickDle(false);
handleOk();
} else {
message.warning(res.message);
}
});
};
const directoryCancel = () => {
form.resetFields();
setIsClickDle(false);
handleCancel();
};
return (
<Modal
title="目录管理"
visible={isModalVisible}
onOk={directorySureEvent}
onCancel={directoryCancel}
>
<Form form={form} labelCol={{ span: 6 }} wrapperCol={{ span: 16 }}>
<Form.Item
label="添加目录"
labelCol={{ span: 4 }}
wrapperCol={{ span: 16 }}
>
<Button icon={<PlusOutlined />} onClick={addDirectoryTitle} />
</Form.Item>
{addOrEditDirectoryList.map((item: categoryDec) => (
<Row key={item.id} gutter={{ xs: 8, sm: 16, md: 24 }}>
<Col span={16}>
<Form.Item
label="目录名称"
name={item.id}
rules={[{ required: true, message: "请输入目录名称" }]}
>
<Input placeholder="请输入目录名称" maxLength={30} />
</Form.Item>
</Col>
<Col>
{item.defaultType ? (
<Button
icon={<MinusOutlined />}
type="primary"
onClick={() => deleteDirectory(item.id)}
/>
) : (
""
)}
</Col>
</Row>
))}
</Form>
</Modal>
);
};
export default AddOrEditDec;
import { Modal } from "antd";
function Index(props: any) {
return (
<Modal
title="分类删除"
visible={props.isDeleteVisable}
onOk={() => props.deleteHandleOk()}
onCancel={() => props.deleteHandleCancel()}
>
<div>确认删除该分类?</div>
</Modal>
);
}
export default Index;
.category-detail{
min-height: 100vh;
background-color: #fff;
padding: 40px;
box-sizing: border-box;
&-head{
display: flex;
justify-content: space-between;
align-items: center;
}
&-title{
font-size: 17px;
font-weight: bold;
}
&-form,&-option,&-table{
margin-top: 20px;
}
}
.add-goods{
&-select{
margin-top: 10px;
&-value{
display: flex;
}
&-list{
height: 300px;
overflow-y: auto;
border: 1px solid #ccc;
margin-top: 10px;
}
}
}
.goods-img{
width: 40px;
height: 40px;
}
\ No newline at end of file
.option-wrap {
padding: 20px;
margin-bottom: 20px;
border-radius: 10px;
}
.row-bg {
background-color: rgba($color: #000000, $alpha: 0.1);
}
.add-children-cgy {
color: rgb(230, 162, 60);
&:hover {
color: rgb(230, 162, 60);
}
}
.category-wrap {
.ant-table-tbody > tr.ant-table-row:hover > td {
background: none !important;
}
}
.table-option-wrap {
display: flex;
justify-content: flex-end;
flex-wrap: wrap;
}
.share-code{
text-align: center;
img {
width: 50%;
height: 50%;
margin-bottom: 10px;
}
.ant-btn span{
text-decoration: underline;
}
}
......@@ -13,6 +13,7 @@ import {
CreditCardOutlined,
SmileOutlined,
TeamOutlined,
ReconciliationOutlined,
} from '@ant-design/icons';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
......@@ -30,6 +31,7 @@ import DivideOrder from '~/pages/pointManage/divideOrder';
import DivideRules from '~/pages/pointManage/divideRules';
// 客户列表
import CustomListView from '~/pages/customManage/customList';
import { CategoryManage } from '~/api';
// 活动
const ActivityList = React.lazy(() => import('src/pages/activityManage/activityList')); //活动管理
// 服务
......@@ -61,6 +63,9 @@ const SplitCouponOperate = React.lazy(
() => import('src/pages/couponManage/splitCouponList/addOrEditOrDetail'),
); // 裂变优惠券操作
const CouponDetailed = React.lazy(() => import('src/pages/couponManage/couponDetailed')); //优惠券明细
// 分类管理
const CategoryList = React.lazy(() => import('src/pages/categoryManage/index'))
export interface RouteObjectType {
path: AgnosticIndexRouteObject['path'];
element: any;
......@@ -405,6 +410,27 @@ export const routerList: Array<RouteObjectType> = [
},
],
},
{
path: '/categoryManage',
element: <LayoutView />,
errorElement: <ErrorPage />,
meta: {
id: 18000,
icon: <ReconciliationOutlined />,
title: '分类管理',
},
children: [
{
path: '/categoryManage/categoryList',
element: withLoadingComponent(<CategoryList />),
meta: {
id: 18100,
title: '分类列表',
icon: <GiftOutlined />,
},
},
],
},
];
// 路由白名单
export const whiteRouterList: Array<RouteObject & RouteObjectType> = [
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论