提交 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
/*
* ~Author: 龚洪江
* ~Date: 2022-07-22 15:13:52
* ~LastEditors: 龚洪江
* ~LastEditTime: 2022-08-12 10:29:36
* ~FilePath: \code\mmc-store\src\pages\goodsManage\children\category\detail\index.tsx
* ~Description:
*
* Copyright (c) 2022 by 龚洪江 2238959530~qq.com, All Rights Reserved.
*/
import { FC, useState, useEffect } from "react";
import {
Form,
Upload,
Button,
Table,
Modal,
Input,
Switch,
message,
} from "antd";
import type { UploadFile } from "antd/es/upload/interface";
import type { ColumnsType, ColumnType } from "antd/es/table";
import type { TableRowSelection } from "antd/es/table/interface";
import {
ArrowUpOutlined,
ArrowDownOutlined,
SearchOutlined,
} from "@ant-design/icons";
import qs from "query-string";
import Viewer from "~/components/viewer";
import { PaginationEntity } from "~/common/interface/PaginationEntity";
import { categoryDetailEntity, GoodsInfo } from "~/api/modules/goods";
import { CategoryManageAPI } from "~/api";
import useOption from "~/common/hook/optionHook";
import "./index.scss";
const CategoryDetail: FC = (props: any) => {
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [addModalVisible, setModalVisible] = useState<boolean>(false);
// 分页数据
const [detailData, setDetailData] = useState<categoryDetailEntity>(null!);
// 全部商品数据
const [allGoodsInfo, setAllGoodsInfo] = useState<GoodsInfo[]>([]);
const [categoryId, setCategoryId] = useState<number>(0);
const [isEdit, setIsEdit] = useState<boolean>(false);
const [detailForm] =
Form.useForm<
Pick<categoryDetailEntity, "description" | "groupName" | "icon">
>();
const [fileList, setFileList] = useState<any>([]);
const [goodsInfo, setGoodsInfo] = useState<GoodsInfo[]>([]);
const [viewerVisible, setViewerVisible] = useState<boolean>(false);
const [pagination, setPagination] = useState<
Pick<PaginationEntity, "pageNo" | "pageSize">
>({
pageNo: 1,
pageSize: 10,
});
const [total, setTotal] = useState<number>(0);
// 当前放大图片
const [currentImgList, setcurrentImgList]: any = useState([]);
const [sortTypeId, setSortTypeId] = useState<number>(-1);
const [pid, setPid] = useState<number>(-1);
const columns: ColumnsType<GoodsInfo> = [
{
title: "序号",
align: "center",
render: (text: string, record: GoodsInfo, index: number) => {
return (pagination.pageNo - 1) * pagination.pageSize + index + 1;
},
},
{
title: "商品名称",
dataIndex: "goodsName",
align: "center",
},
{
title: "商品图片",
align: "center",
render: (text: string, record: GoodsInfo, index: number) => {
return (
<img
className="goods-img"
src={record.goodsImg}
onClick={() => showViewerEvent(record.goodsImg)}
/>
);
},
},
{
title: "状态",
align: "center",
render: (text: string, record: GoodsInfo, index: number) => {
return <div>{record.shelfStatus ? "上架" : "下架"}</div>;
},
},
{
title: "安全编码开关",
align: "center",
render: (text: string, record: GoodsInfo, index: number) => {
return (
btnCode && (
<Switch
checked={!!record.showCode}
onChange={(checked, e) =>
switchChangeEvent(checked, e, record, index)
}
/>
)
);
},
},
// {
// title: "操作",
// align: "center",
// filterDropdownVisible:false,
// render: (text: string, record: GoodsInfo, index: number) => {
// return (
// <Button type="link" danger onClick={() => deleteGoods(record)}>
// 删除
// </Button>
// );
// },
// },
];
// 按钮权限
const btnUpperDown = useOption(22109);
const btnChange = useOption(22110);
const btnCode = useOption(22111);
useEffect(() => {
// 分页数据
getGoodsTypeDetail(props.match.params.id);
}, [pagination]);
useEffect(() => {
// 全部数据
getAllGoodsTypeDetail(props.match.params.id);
setSortTypeId(Number(qs.parse(props.location.search).sortTypeId));
setPid(Number(qs.parse(props.location.search).pid));
setCategoryId(props.match.params.id);
}, []);
const backRoute = () => {
props.history.go(-1);
};
// 关联商品新增
const addModalShowEvent = () => {
setModalVisible(true);
};
const addConnectGoods = () => {
setModalVisible(false);
};
const cancelHandle = () => {
setModalVisible(false);
};
// 分页数据
const getGoodsTypeDetail = async (id: number) => {
const res: any = await CategoryManageAPI.getGoodsTypeDetail({ id, ...pagination });
detailForm.setFieldsValue({
groupName: res.result.groupName ? res.result.groupName : undefined,
description: res.result.description ? res.result.description : undefined,
icon: res.result.icon ? res.result.icon : "",
});
if (res.result.icon) {
setFileList([
{
id: new Date().getTime(),
status: "done",
url: res.result.icon,
},
]);
}
setDetailData(res.result);
setGoodsInfo(res.result.goodsInfo || []);
setTotal(res.result.totalCount);
};
// 全部数据
const getAllGoodsTypeDetail = async (id: number) => {
const res: any = await CategoryManageAPI.getGoodsTypeDetail({
id,
pageNo: 1,
pageSize: 99999,
});
setAllGoodsInfo(res.result.goodsInfo || []);
};
// 自定义上传
const selfUploadRequest = async (val: any) => {
const formData: FormData = new FormData();
formData.append("uploadFile", val.file);
const res: any = await CategoryManageAPI.cateGoryDetailUpload(formData);
if (res.code === "200") {
setFileList([
{
id: new Date().getTime(),
status: "done",
url: res.result[0],
},
]);
detailForm.setFieldsValue({ icon: res.result[0] });
} else {
message.error(res.message);
}
};
const onRemove = (file: UploadFile) => {
if (!isEdit) {
return Promise.resolve(false);
}
setFileList([]);
detailForm.setFieldsValue({
icon: "",
});
};
const beforeUpload = (val: any) => {
if (!["image/jpg", "image/jpeg", "image/png"].includes(val.type)) {
message.error("请上传图片格式为jpg,jpeg,png的图片");
return false;
}
const limitM = 2;
const isLimit: boolean = val.size / 1024 / 1024 <= limitM;
if (!isLimit) {
message.error("图片最大上传2M");
return false;
}
return true;
};
// const iconValidator=(rule:any,value:any)=>{
// console.log('数据--->',value)
// }
const onPreview = (file: UploadFile) => {
showViewerEvent(file.url as string);
};
// 安全编码
const switchChangeEvent = async (
checked: boolean,
e: React.MouseEvent<HTMLButtonElement>,
record: GoodsInfo,
index: number
) => {
record.showCode = checked ? 1 : 0;
// goodsInfo.splice(index, 1, record);
// setDetailData({ ...detailData });
// setGoodsInfo([...goodsInfo]);
const res: any = await CategoryManageAPI.updateIsShowCode({
goodsId: record.goodsId,
showCode: record.showCode,
});
if (res.code === "200") {
message.success("修改成功");
getGoodsTypeDetail(props.match.params.id);
}
};
// 商品删除
const deleteGoods = (record: GoodsInfo) => {};
// 修改详情
const changeDetailInfo = () => {
setIsEdit(true);
};
const sureChangeDetailInfo = () => {
detailForm
.validateFields()
.then(
async (
value: Pick<
categoryDetailEntity,
"description" | "groupName" | "icon"
>
) => {
// console.log("数据--->", value);
const res = await CategoryManageAPI.updateClassification({
// description:value.description,
// groupName:value.groupName,
// icon:fileList.length?fileList[0].url:'',
...value,
sortTypeId,
pid,
id: categoryId,
});
if (res.code === "200") {
message.success("修改成功");
getGoodsTypeDetail(categoryId);
setIsEdit(false);
} else {
message.error(res.message);
}
}
);
};
// 上移
const shiftGoods = async () => {
if (selectedRowKeys.length === 0) {
message.warning("请先选择商品");
} else {
const allIndex: number = allGoodsInfo.findIndex(
(item: GoodsInfo) => item.goodsId === selectedRowKeys[0]
);
const index: number = goodsInfo.findIndex(
(item: GoodsInfo) => item.goodsId === selectedRowKeys[0]
);
if (index === 0 && pagination.pageNo === 1) {
message.warning("已经到最上面了!");
} else {
const res: any = await CategoryManageAPI.exchangeGoodsInfo(
allGoodsInfo[allIndex].goodsId,
allGoodsInfo[allIndex - 1].goodsId
);
if (res.code === "200") {
message.success("上移成功");
if (index === 0) {
pagination.pageNo--;
}
setPagination({ ...pagination });
getAllGoodsTypeDetail(categoryId);
// let obj: GoodsInfo = goodsInfo[index];
// goodsInfo.splice(index, 1, goodsInfo[index - 1]);
// goodsInfo.splice(index - 1, 1, obj);
// setGoodsInfo([...goodsInfo]);
}
}
}
};
// 下移
const downGoods = async () => {
if (selectedRowKeys.length === 0) {
message.warning("请先选择商品");
} else {
const allIndex: number = allGoodsInfo.findIndex(
(item: GoodsInfo) => item.goodsId === selectedRowKeys[0]
);
const index: number = goodsInfo.findIndex(
(item: GoodsInfo) => item.goodsId === selectedRowKeys[0]
);
if (allIndex === allGoodsInfo.length - 1) {
message.warning("已经到最下面了!");
} else {
const res: any = await CategoryManageAPI.exchangeGoodsInfo(
allGoodsInfo[index].goodsId,
allGoodsInfo[index + 1].goodsId
);
if (res.code === "200") {
message.success("下移成功");
if (index === goodsInfo.length - 1) {
pagination.pageNo++;
}
setPagination({ ...pagination });
getAllGoodsTypeDetail(categoryId);
// let obj: GoodsInfo = goodsInfo[index];
// goodsInfo.splice(index, 1, goodsInfo[index + 1]);
// goodsInfo.splice(index + 1, 1, obj);
// setGoodsInfo([...goodsInfo]);
}
}
}
};
// 分页
const paginationChange = (pageNo: number, pageSize: number) => {
setPagination({ pageNo, pageSize });
};
const showViewerEvent = (src: string) => {
setcurrentImgList([{ src }]);
setViewerVisible(true);
};
const setVisibleEvent = () => {
setViewerVisible(false);
};
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
setSelectedRowKeys(newSelectedRowKeys);
};
const rowSelection: TableRowSelection<GoodsInfo> = {
selectedRowKeys,
onChange: onSelectChange,
type: "radio",
};
return (
<div className="category-detail">
<div className="category-detail-head">
<div className="category-detail-title">一、基本信息</div>
<div className="category-detail-head-option">
{isEdit ? (
<Button
type="primary"
style={{ marginRight: "10px" }}
onClick={sureChangeDetailInfo}
>
保存
</Button>
) : btnChange ? (
<Button
type="primary"
style={{ marginRight: "10px" }}
onClick={changeDetailInfo}
>
修改
</Button>
) : (
""
)}
<Button type="primary" onClick={backRoute}>
返回
</Button>
</div>
</div>
<div className="category-detail-form">
<Form form={detailForm} labelCol={{ span: 2 }} wrapperCol={{ span: 5 }}>
<Form.Item
label="行业名称"
name="groupName"
rules={[{ required: true, message: "请输入行业名称" }]}
>
{isEdit ? (
<Input
placeholder="请输入行业名称"
disabled={!isEdit}
maxLength={15}
/>
) : (
<span>{detailData?.groupName}</span>
)}
</Form.Item>
<Form.Item label="分类描述" name="description">
{isEdit ? (
<Input
placeholder="请输入分类描述"
disabled={!isEdit}
maxLength={70}
/>
) : (
<span>{detailData?.description}</span>
)}
</Form.Item>
{detailData?.icon ? (
<Form.Item
label="分类图标"
name="icon"
rules={[{ required: true, message: "请上传分类图标" }]}
>
{/* 外层嵌套解决错乱问题和影响form数据问题 */}
<div key={Math.random()}>
<Upload
listType="picture-card"
fileList={fileList}
customRequest={selfUploadRequest}
onRemove={onRemove}
onPreview={onPreview}
maxCount={1}
accept="image/*"
beforeUpload={beforeUpload}
>
{fileList.length < 1 ? <div>上传</div> : ""}
</Upload>
</div>
</Form.Item>
) : (
""
)}
</Form>
</div>
<div className="category-detail-title" style={{ marginTop: "20px" }}>
二、关联商品
</div>
<div className="category-detail-option">
{btnUpperDown ? (
<>
<Button
icon={<ArrowUpOutlined />}
type="primary"
style={{ marginRight: "10px" }}
onClick={shiftGoods}
/>
<Button
icon={<ArrowDownOutlined />}
type="primary"
style={{ marginRight: "10px" }}
onClick={downGoods}
/>
</>
) : (
""
)}
{/* <Button type="primary" onClick={addModalShowEvent}>
添加
</Button> */}
</div>
<div className="category-detail-table">
<Table
size="small"
columns={
Number(qs.parse(props.location.search).sortTypeId) === 1
? columns.filter(
(item: ColumnType<GoodsInfo>) => item.title != "安全编码开关"
)
: columns
}
dataSource={goodsInfo}
pagination={{
total,
pageSize: pagination.pageSize,
current: pagination.pageNo,
showSizeChanger: true,
onChange: (page, pageSize) => paginationChange(page, pageSize),
showTotal: (total, range) =>
`当前 ${range[0]}-${range[1]} 条记录 / 共 ${total} 条数据`,
}}
bordered
rowKey="goodsId"
rowSelection={rowSelection}
/>
</div>
<Modal
visible={addModalVisible}
title="关联商品新增"
onOk={addConnectGoods}
onCancel={cancelHandle}
>
{/* <Form form={connectForm}>
<Form.Item label="商品">
<Select placeholder="请选择商品" mode="multiple">
<Select.Option value={1}>{1}</Select.Option>
<Select.Option value={2}>{2}</Select.Option>
</Select>
</Form.Item>
</Form> */}
<div className="add-goods">
<div>
<Input suffix={<SearchOutlined />} placeholder="请输入商品名称" />
</div>
<div className="add-goods-select">
<div className="add-goods-select-value">
<div className="label">您选择的商品是:</div>
<div className="select-value" />
</div>
<div className="add-goods-select-list" />
</div>
</div>
</Modal>
{/* 图片放大 */}
<Viewer
visible={viewerVisible}
currentImgList={currentImgList}
activeViewerIndex={0}
setVisible={setVisibleEvent}
/>
</div>
);
};
export default CategoryDetail;
.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;
}
}
import { FC, useEffect, useState, useRef } from 'react';
import { Button, Table, Form, message, Tooltip, Checkbox, Card, Modal } from 'antd';
import { CardTabListType } from 'antd/lib/card/Card';
import {Router} from 'react-router-dom';
import FileSaver from 'file-saver';
import qs from 'query-string';
import {
PlusOutlined,
DeleteOutlined,
EditOutlined,
AppstoreAddOutlined,
ArrowUpOutlined,
ArrowDownOutlined,
RightOutlined,
DownOutlined,
ShareAltOutlined,
ProfileOutlined,
} from '@ant-design/icons';
import AddCgyDailog from './components/addCgyDailog';
import DeleteCgyDailog from './components/deleteCgyDailog';
import EditableCell from './components/EditableCell';
import AddOrEditDec from './components/addOrEditDec';
import { CategoryManageAPI } from '~/api';
import { categoryEntity, categoryDec } from '~/api/interface/categoryManage'
import Viewer from '~/components/viewer';
import useOption from '~/common/hook/optionHook';
import './index.scss';
import events from '~/events';
const Category: FC = (props: any) => {
const baseRef: any = useRef();
// 分类目录
const [tabList, setTabList] = useState<CardTabListType[]>([]);
const [directoryList, setDirectoryList] = useState<categoryDec[]>([]);
const [isAddOrEditDecModal, setIsAddOrEditDecModal] = useState<boolean>(false);
// 分类列表
const [categoryList, setCategoryList] = useState<categoryEntity[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
// 子分类表单
const [form] = Form.useForm();
// 展开的行
const [expandedRowKeys, setExpandedRowKeys] = useState<(number | string)[]>([]);
// 编辑的行
const [editingKey, setEditingKey]: any = useState([]);
const isEditing = (record: categoryEntity) => {
const index: number = editingKey.findIndex((item: any) => item == record.goodsMasterTypeId);
if (index != -1) {
return true;
}
return false;
};
const columns = [
{
title: '分类名称',
dataIndex: 'goodsMasterType',
key: 'goodsMasterType',
// align: "center",
width: '12%',
editable: true,
ellipsis: true,
render: (text: string, record: categoryEntity, index: number) => {
return (
<Tooltip placement='top' title={<span>{record.goodsMasterType}</span>}>
<span
style={{
marginLeft:
record.goodsSlaveTypeDTO && record.goodsSlaveTypeDTO.length != 0
? '10px'
: '24px',
}}
>
{record.goodsMasterType}
</span>
</Tooltip>
);
},
onHeaderCell: () => {
return {
style: {
textAlign: 'center',
},
};
},
},
{
title: '图片',
// dataIndex: "icon",
key: 'icon',
width: '12%',
align: 'center',
render: (text: string, record: categoryEntity, index: number) => {
return record.icon ? (
<img
src={record.icon}
style={{ width: '50px', height: '50px' }}
onClick={() => imgClick(record.icon)}
/>
) : (
''
);
},
},
{
title: '描述',
key: 'desc',
dataIndex: 'desc',
width: '20%',
align: 'center',
editable: true,
ellipsis: true,
render: (text: string, record: categoryEntity, index: number) => {
return (
<Tooltip placement='right' title={<span>{record.desc}</span>}>
<span>{record.desc}</span>
</Tooltip>
);
},
},
{
title: '创建时间',
dataIndex: 'createTime',
width: '15%',
key: 'createTime',
align: 'center',
},
{
title: '操作',
width: '55%',
key: 'option',
align: 'center',
render: (text: string, record: categoryEntity, index: number) => {
return (
<div className='table-option-wrap'>
{record.goodsSlaveTypeDTO ? (
<>
{btnAddChildCgy ? (
<Button
type='link'
onClick={() => {
addChildrenCgy(record);
}}
className='add-children-cgy'
icon={<AppstoreAddOutlined />}
>
新增子分类
</Button>
) : (
''
)}
{activeTabKey === 2 && btnShareCode ? (
<Button
type='link'
icon={<ShareAltOutlined />}
onClick={() => showShareCode(record)}
>
分享码
</Button>
) : (
''
)}
</>
) : (
''
)}
{record.isADD ? (
<>
<Button type='link' onClick={() => sureAddChildrenCgy(record)}>
确定
</Button>
<Button type='link' onClick={() => cancelAddCldCgy(record, index)}>
取消
</Button>
</>
) : (
<>
{btnEdit ? (
<Button type='link' onClick={() => editCgy(record)} icon={<EditOutlined />}>
编辑
</Button>
) : (
''
)}
{btnDetail ? (
<Button
type='link'
icon={<ProfileOutlined />}
onClick={() => toCategoryDetail(record)}
>
详情
</Button>
) : (
''
)}
{btnDelete ? (
<Button
type='link'
onClick={() => deleteCgy(record)}
danger
icon={<DeleteOutlined />}
>
删除
</Button>
) : (
''
)}
</>
)}
</div>
);
},
},
];
const mergedColumns = columns.map((col: any) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: categoryEntity, index: number) => ({
record,
index,
inputType: 'textarea',
dataIndex: col.dataIndex,
title: col.title,
editing: isEditing(record),
}),
};
});
// 图片放大
const [visible, setvisible] = useState<boolean>(false);
// 当前放大图片
const [currentImgList, setcurrentImgList] = useState<{ src: string }[]>([]);
// 当前图片下标
const [activeViewerIndex] = useState<number>(0);
// 新增弹窗
const [isVisible, setIsVisible] = useState<boolean>(false);
// 删除弹窗
const [isDeleteVisible, setIsDeleteVisible] = useState<boolean>(false);
// 删除分类id
const [cgyId, setCgyId] = useState<number>(0);
// 弹窗title
const [title, setTitle] = useState<string>('');
// 当前修改的行
const [currentRecord, setCurrentRecord]: any = useState({});
// 是否编辑
const [isEdit, setIsEdit] = useState<boolean>(false);
const [selectedRowKeys, setSelectedRowKeys] = useState<any>([]);
const [activeTabKey, setActiveTabKey] = useState<number>(2);
// 分享码弹窗显示/隐藏
const [isShareCodeVble, setShareCodeVble] = useState<boolean>(false);
const [shareCodeUrl, setShareCodeUrl] = useState<string>('');
// 按钮权限
const btnAdd = useOption(22101);
const btnUpperDown = useOption(22102);
const btnDir = useOption(22103);
const btnAddChildCgy = useOption(22104);
const btnShareCode = useOption(22105);
const btnEdit = useOption(22106);
const btnDelete = useOption(22107);
const btnDetail = useOption(22108);
// 获取请求数据
useEffect(() => {
if (qs.parse(props.location?.search).activeTabKey) {
// console.log("当前数据--->", qs.parse(props.location?.search).activeTabKey);
setActiveTabKey(Number(qs.parse(props.location?.search).activeTabKey));
}
getCategoryList(Number(qs.parse(props.location?.search).activeTabKey) || Number(activeTabKey));
getDirectoryList();
}, []);
// 获取分类目录
const getDirectoryList = async () => {
const res: any = await CategoryManageAPI.directoryList();
const tabList: CardTabListType[] = res.result.map((item: categoryDec) => {
return {
key: item.id.toString(),
tab: item.sortName,
};
});
setTabList([...tabList]);
setDirectoryList([...res.result]);
};
// 分类列表
const getCategoryList = async (type: number) => {
setIsLoading(true);
const res = await CategoryManageAPI.getListGoodsTypeList(type);
setIsLoading(false);
setCategoryList(res.result || []);
const arr: number[] = res.result.map((item: any) => {
return item.goodsMasterTypeId;
});
setExpandedRowKeys([...arr]);
};
// 新增分类弹窗
const addCayDailogShow = () => {
setTitle('新增分类');
setIsVisible(true);
};
const handleOk = () => {
const addForm = baseRef.current.getForm();
if (title === '新增分类') {
addForm
.validateFields()
.then(async (values: any) => {
const res = await CategoryManageAPI.addClassification({
...values,
pid: 0,
sortTypeId: activeTabKey,
});
if (res.code == 200) {
events.emit('removeFileList');
message.success('新增成功');
setIsVisible(false);
getCategoryList(Number(activeTabKey));
addForm.resetFields();
}
})
.catch((err: any) => {});
} else {
addForm
.validateFields()
.then(async (values: any) => {
const res = await CategoryManageAPI.updateClassification({
...values,
id: currentRecord.goodsMasterTypeId,
pid: currentRecord.pid,
sortTypeId: activeTabKey,
});
if (res.code === '200') {
message.success('修改成功');
getCategoryList(Number(activeTabKey));
setIsVisible(false);
events.emit('removeFileList');
addForm.resetFields();
}
})
.catch((err: any) => {});
}
};
const handleCancel = () => {
const addForm = baseRef.current.getForm();
addForm.resetFields();
events.emit('setImgFile', '');
events.emit('removeFileList');
setIsVisible(false);
};
// 新增子分类
const addChildrenCgy = (record: categoryEntity) => {
if (editingKey.length === 0) {
const item: categoryEntity | undefined = categoryList.find(
(item: categoryEntity) => item.goodsMasterTypeId === record.goodsMasterTypeId,
);
item?.goodsSlaveTypeDTO.push({
goodsMasterTypeId: `${record.goodsMasterTypeId}self`,
goodsMasterType: '',
desc: '',
isADD: true,
pid: record.goodsMasterTypeId,
});
setCategoryList([...categoryList]);
form.setFieldsValue({ goodsMasterType: '', desc: '' });
editingKey.push(`${record.goodsMasterTypeId}self`);
setEditingKey(editingKey);
setIsEdit(false);
}
const bol = expandedRowKeys.some((item: any) => item == record.goodsMasterTypeId);
if (!bol) {
const arr: any = [];
expandedRowKeys.push(record.goodsMasterTypeId);
arr.push(...expandedRowKeys);
setExpandedRowKeys(arr);
}
};
// 展开监听
const onExpand = (expanded: boolean, record: categoryEntity) => {
const index = expandedRowKeys.findIndex((item: any) => item === record.goodsMasterTypeId);
if (index != -1) {
expandedRowKeys.splice(index, 1);
setExpandedRowKeys(expandedRowKeys);
} else {
expandedRowKeys.push(record.goodsMasterTypeId);
setExpandedRowKeys(expandedRowKeys);
}
};
// 新增子分类
const sureAddChildrenCgy = (record: categoryEntity) => {
let pid: string | number = -1;
categoryList.map((item: categoryEntity) => {
if (item.goodsSlaveTypeDTO && item.goodsSlaveTypeDTO.length != 0) {
const bol: boolean = item.goodsSlaveTypeDTO.some(
(item1: Partial<categoryEntity>) => item1.goodsMasterTypeId === record.goodsMasterTypeId,
);
if (bol) {
pid = item.goodsMasterTypeId;
}
}
});
form.validateFields().then(async (val: any) => {
const obj: any = {
pid,
groupName: val.goodsMasterType,
description: val.desc,
sortTypeId: activeTabKey,
};
if (isEdit) {
const res = await CategoryManageAPI.updateClassification({
...obj,
id: record.goodsMasterTypeId,
});
if (res.code === '200') {
message.success('修改成功');
setEditingKey([]);
getCategoryList(Number(activeTabKey));
form.resetFields();
} else {
message.warning(res.message);
}
} else {
const res = await CategoryManageAPI.addClassification(obj);
if (res.code == 200) {
message.success('新增成功');
setEditingKey([]);
getCategoryList(Number(activeTabKey));
form.resetFields();
} else {
message.warning(res.message);
}
}
});
};
// 取消新增子分类
const cancelAddCldCgy = (record: categoryEntity, index: number) => {
categoryList.map((item: categoryEntity) => {
if (item.goodsSlaveTypeDTO && item.goodsSlaveTypeDTO.length != 0) {
const index: number = item.goodsSlaveTypeDTO.findIndex(
(item1: Partial<categoryEntity>) => item1.goodsMasterTypeId === record.goodsMasterTypeId,
);
if (index != -1) {
if (isEdit) {
item.goodsSlaveTypeDTO[index].isADD = false;
setIsEdit(false);
} else {
item.goodsSlaveTypeDTO.splice(index, 1);
}
}
}
});
setEditingKey([]);
setCategoryList([...categoryList]);
};
// 删除分类
const deleteCgy = (record: categoryEntity) => {
setCgyId(Number(record.goodsMasterTypeId));
setIsDeleteVisible(true);
};
// 删除弹窗确认
const deleteHandleOk = async () => {
const bol = categoryList.some((item: categoryEntity) => item.goodsMasterTypeId === cgyId);
if (bol) {
const res = await CategoryManageAPI.deleteGoodsTypeByOneLevel({
id: cgyId,
sortTypeId: activeTabKey,
});
if (res.code === '200') {
message.success('删除成功');
getCategoryList(Number(activeTabKey));
} else {
message.warning(res.message);
}
} else {
const res = await CategoryManageAPI.deleteGoodsTypeByChildren({
id: cgyId,
sortTypeId: activeTabKey,
});
if (res.code === '200') {
message.success('删除成功');
getCategoryList(Number(activeTabKey));
} else {
message.warning(res.message);
}
}
setIsDeleteVisible(false);
};
// 删除弹框取消
const deleteHandleCancel = () => {
setIsDeleteVisible(false);
};
// 编辑分类
const editCgy = (record: categoryEntity) => {
setCurrentRecord(record);
const bol = categoryList.some(
(item: categoryEntity) => item.goodsMasterTypeId === record.goodsMasterTypeId,
);
if (bol) {
setTitle('修改分类');
const addForm = baseRef.current.getForm();
addForm.setFieldsValue({
groupName: record.goodsMasterType,
description: record.desc,
remark: record.remark,
});
events.emit('setImgFile', record.icon);
setIsVisible(true);
} else {
// 编辑状态
setIsEdit(true);
categoryList.map((item: categoryEntity) => {
if (item.goodsSlaveTypeDTO && item.goodsSlaveTypeDTO.length != 0) {
const index: number = item.goodsSlaveTypeDTO.findIndex(
(item1: Partial<categoryEntity>) =>
item1.goodsMasterTypeId === record.goodsMasterTypeId,
);
if (index != -1) {
item.goodsSlaveTypeDTO[index].isADD = true;
}
}
});
setCategoryList([...categoryList]);
setEditingKey([record.goodsMasterTypeId]);
form.setFieldsValue(record);
}
};
const setVisibleEvent = () => {
setvisible(false);
};
const imgClick = (str: string) => {
setcurrentImgList([{ src: str }]);
setvisible(true);
};
// 排序选择
const onSelectChange = (record: categoryEntity) => {
const index: number = selectedRowKeys.findIndex(
(item: any) => item === record.goodsMasterTypeId,
);
if (index != -1) {
selectedRowKeys.splice(index, 1);
setSelectedRowKeys([...selectedRowKeys]);
} else {
setSelectedRowKeys([...selectedRowKeys, record.goodsMasterTypeId]);
}
};
// 上移
const upperEvent = async () => {
if (selectedRowKeys.length === 0) {
message.error('请选择分类');
} else if (selectedRowKeys.length === 2) {
message.error('分类只能选择一个');
} else {
const index: number = categoryList.findIndex(
(item: categoryEntity) => item.goodsMasterTypeId === selectedRowKeys[0],
);
if (index === 0) {
message.error('已经是最上面了');
} else {
const res = await CategoryManageAPI.exchangeSortType({
firstId: selectedRowKeys[0],
secondId: categoryList[index - 1].goodsMasterTypeId,
sortTypeId: activeTabKey,
});
if (res.code === '200') {
getCategoryList(Number(activeTabKey));
message.success('上移成功');
}
}
}
};
// 下移
const shiftEvent = async () => {
if (selectedRowKeys.length === 0) {
message.error('请选择分类');
} else if (selectedRowKeys.length === 2) {
message.error('分类只能选择一个');
} else {
const index = categoryList.findIndex(
(item: categoryEntity) => item.goodsMasterTypeId === selectedRowKeys[0],
);
if (index === categoryList.length - 1) {
message.error('已经是最下面了');
} else {
const res = await CategoryManageAPI.exchangeSortType({
firstId: selectedRowKeys[0],
secondId: categoryList[index + 1].goodsMasterTypeId,
sortTypeId: activeTabKey,
});
if (res.code === '200') {
getCategoryList(Number(activeTabKey));
message.success('下移成功');
}
}
}
};
const rowSelection: any = {
selectedRowKeys,
onChange: onSelectChange,
hideSelectAll: true,
renderCell: (checked: boolean, record: categoryEntity) => {
return (
<>
{record.goodsSlaveTypeDTO != null ? (
<Checkbox onChange={() => onSelectChange(record)} />
) : (
''
)}
</>
);
},
};
// 页签改变监听
const handleTabChange = (key: string) => {
Router.push({
pathname: '/goodsManage/category',
search: qs.stringify({ activeTabKey: key }),
});
setSelectedRowKeys([]);
setActiveTabKey(Number(key));
getCategoryList(Number(key));
};
// 分类详情
const toCategoryDetail = (record: categoryEntity) => {
Router.push({
pathname: `/goodsManage/category/detail/${record.goodsMasterTypeId}`,
search: qs.stringify({ sortTypeId: activeTabKey, pid: record.pid }),
});
};
// 分享码弹窗显示
const showShareCode = async (record: any) => {
const res: any = await CategoryManageAPI.getAppletQRCode({
page: 'page-sort/sortDetail/index',
scene: `?id=${record.goodsMasterTypeId}&backTip=1`,
});
setShareCodeUrl(`data:image/png;base64,${res.result}`);
setShareCodeVble(true);
};
const shareCodeHandleCancel = () => {
setShareCodeVble(false);
};
// 行业分享码下载
const downLoadCode = () => {
FileSaver.saveAs(shareCodeUrl, '无人机城行业分享码');
};
// 目录弹窗
const showDecModal = () => {
setDirectoryList([...directoryList]);
setIsAddOrEditDecModal(true);
};
const addOrEditDecHandleOk = () => {
getDirectoryList();
setIsAddOrEditDecModal(false);
};
const addOrEditDecHandleCancel = () => {
setIsAddOrEditDecModal(false);
};
const refreshDec = (id: number) => {
const index: number = directoryList.findIndex((item: categoryDec) => item.id === activeTabKey);
if (id === directoryList[index].id) {
setActiveTabKey(directoryList[index - 1].id);
Router.push({
pathname: '/goodsManage/category',
search: qs.stringify({ activeTabKey: directoryList[index - 1].id }),
});
getCategoryList(directoryList[index - 1].id);
}
getDirectoryList();
// if (qs.parse(props.location?.search).activeTabKey) {
// setActiveTabKey(Number(qs.parse(props.location?.search).activeTabKey));
// }
};
return (
<div className='category-wrap'>
<div className='option-wrap'>
{btnAdd ? (
<Button
type='primary'
style={{ marginRight: '10px' }}
onClick={() => addCayDailogShow()}
icon={<PlusOutlined />}
>
新增分类
</Button>
) : (
''
)}
{btnUpperDown ? (
<>
{' '}
{/* 上移 */}
<Button
icon={<ArrowUpOutlined />}
type='primary'
style={{ marginRight: '10px' }}
onClick={upperEvent}
/>
{/* 下移 */}
<Button
icon={<ArrowDownOutlined />}
type='primary'
style={{ marginRight: '10px' }}
onClick={shiftEvent}
/>
</>
) : (
''
)}
</div>
<Card
style={{ width: '100%' }}
tabList={tabList}
activeTabKey={String(activeTabKey)}
onTabChange={(key) => handleTabChange(key)}
tabBarExtraContent={
btnDir ? (
<Button type='link' onClick={showDecModal}>
目录管理
</Button>
) : (
''
)
}
>
<Form form={form} component={false}>
<Table
size='small'
columns={mergedColumns}
dataSource={categoryList}
rowSelection={rowSelection}
rowKey='goodsMasterTypeId'
bordered
loading={isLoading}
rowClassName={(record: any, index: number) => {
if (record.goodsSlaveTypeDTO) {
return '';
}
return 'row-bg';
}}
expandedRowKeys={expandedRowKeys}
onExpand={(expanded: boolean, record: any) => onExpand(expanded, record)}
components={{
body: {
cell: EditableCell,
},
}}
childrenColumnName='goodsSlaveTypeDTO'
expandable={{
rowExpandable: (record) => {
if (record.goodsSlaveTypeDTO && record.goodsSlaveTypeDTO.length != 0) {
return true;
}
return false;
},
expandIcon: ({ expanded, onExpand, record }) => {
if (expanded && record.goodsSlaveTypeDTO && record.goodsSlaveTypeDTO.length != 0) {
return (
<DownOutlined
onClick={(e) => onExpand(record, e)}
// style={{marginRight:"10px"}}
/>
);
}
if (record.goodsSlaveTypeDTO && record.goodsSlaveTypeDTO.length != 0) {
return (
<RightOutlined
onClick={(e) => onExpand(record, e)}
// style={{marginRight:"10px"}}
/>
);
}
return <></>;
},
}}
pagination={false}
/>
</Form>
</Card>
<AddCgyDailog
title={title}
isVisable={isVisible}
handleOk={handleOk}
handleCancel={handleCancel}
baseRef={baseRef}
imgClick={imgClick}
/>
<DeleteCgyDailog
isDeleteVisable={isDeleteVisible}
deleteHandleOk={deleteHandleOk}
deleteHandleCancel={deleteHandleCancel}
/>
{/* 图片放大 */}
<Viewer
visible={visible}
currentImgList={currentImgList}
activeViewerIndex={activeViewerIndex}
setVisible={setVisibleEvent}
/>
{/* 目录管理 */}
<AddOrEditDec
isModalVisible={isAddOrEditDecModal}
handleOk={addOrEditDecHandleOk}
handleCancel={addOrEditDecHandleCancel}
directoryList={directoryList}
refreshDec={refreshDec}
/>
{/* 分享码弹窗 */}
<Modal
visible={isShareCodeVble}
title='分享码'
footer={null}
onCancel={shareCodeHandleCancel}
>
<div className='share-code'>
<img src={shareCodeUrl} alt='' onClick={() => imgClick(shareCodeUrl)} />
<div>
<Button type='link' onClick={downLoadCode}>
下载
</Button>
</div>
</div>
</Modal>
</div>
);
};
export default Category;
......@@ -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> = [
......
......@@ -653,6 +653,13 @@
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
"@types/quill@^1.3.10":
version "1.3.10"
resolved "https://registry.yarnpkg.com/@types/quill/-/quill-1.3.10.tgz#dc1f7b6587f7ee94bdf5291bc92289f6f0497613"
integrity sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==
dependencies:
parchment "^1.1.2"
"@types/react-dom@^18.0.5":
version "18.0.5"
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.5.tgz"
......@@ -1075,6 +1082,11 @@ classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classna
resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz"
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
clone@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
codepage@~1.15.0:
version "1.15.0"
resolved "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz"
......@@ -1200,6 +1212,18 @@ decode-uri-component@^0.4.1:
resolved "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz"
integrity sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==
deep-equal@^1.0.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==
dependencies:
is-arguments "^1.0.4"
is-date-object "^1.0.1"
is-regex "^1.0.4"
object-is "^1.0.1"
object-keys "^1.1.1"
regexp.prototype.flags "^1.2.0"
deep-is@^0.1.3:
version "0.1.4"
resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz"
......@@ -1213,6 +1237,14 @@ define-properties@^1.1.3, define-properties@^1.1.4:
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"
define-properties@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5"
integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==
dependencies:
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz"
......@@ -1549,11 +1581,31 @@ esutils@^2.0.2:
resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
eventemitter3@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
integrity sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==
events@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
extend@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-diff@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154"
integrity sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==
fast-diff@^1.1.2:
version "1.2.0"
resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz"
......@@ -1596,7 +1648,7 @@ file-entry-cache@^6.0.1:
file-saver@^2.0.5:
version "2.0.5"
resolved "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz"
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
fill-range@^7.0.1:
......@@ -1680,9 +1732,9 @@ functional-red-black-tree@^1.0.1:
resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz"
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
functions-have-names@^1.2.2:
functions-have-names@^1.2.2, functions-have-names@^1.2.3:
version "1.2.3"
resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz"
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
gensync@^1.0.0-beta.2:
......@@ -1870,6 +1922,14 @@ internal-slot@^1.0.3:
has "^1.0.3"
side-channel "^1.0.4"
is-arguments@^1.0.4:
version "1.1.1"
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
dependencies:
call-bind "^1.0.2"
has-tostringtag "^1.0.0"
is-bigint@^1.0.1:
version "1.0.4"
resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz"
......@@ -1940,9 +2000,9 @@ is-number@^7.0.0:
resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
is-regex@^1.1.4:
is-regex@^1.0.4, is-regex@^1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
dependencies:
call-bind "^1.0.2"
......@@ -2092,7 +2152,7 @@ lodash.merge@^4.6.2:
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash@^4.17.21:
lodash@^4.17.21, lodash@^4.17.4:
version "4.17.21"
resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
......@@ -2206,6 +2266,14 @@ object-inspect@^1.12.0, object-inspect@^1.9.0:
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz"
integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
object-is@^1.0.1:
version "1.1.5"
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==
dependencies:
call-bind "^1.0.2"
define-properties "^1.1.3"
object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz"
......@@ -2299,6 +2367,11 @@ p-try@^1.0.0:
resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz"
integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
parchment@^1.1.2, parchment@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/parchment/-/parchment-1.1.4.tgz#aeded7ab938fe921d4c34bc339ce1168bc2ffde5"
integrity sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
......@@ -2417,6 +2490,27 @@ queue-microtask@^1.2.2:
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
quill-delta@^3.6.2:
version "3.6.3"
resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-3.6.3.tgz#b19fd2b89412301c60e1ff213d8d860eac0f1032"
integrity sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==
dependencies:
deep-equal "^1.0.1"
extend "^3.0.2"
fast-diff "1.1.2"
quill@^1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/quill/-/quill-1.3.7.tgz#da5b2f3a2c470e932340cdbf3668c9f21f9286e8"
integrity sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==
dependencies:
clone "^2.1.1"
deep-equal "^1.0.1"
eventemitter3 "^2.0.3"
extend "^3.0.2"
parchment "^1.1.4"
quill-delta "^3.6.2"
rc-align@^4.0.0:
version "4.0.15"
resolved "https://registry.npmjs.org/rc-align/-/rc-align-4.0.15.tgz"
......@@ -2803,6 +2897,15 @@ react-is@^18.0.0:
resolved "https://registry.npmmirror.com/react-is/-/react-is-18.2.0.tgz"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react-quill@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/react-quill/-/react-quill-2.0.0.tgz#67a0100f58f96a246af240c9fa6841b363b3e017"
integrity sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==
dependencies:
"@types/quill" "^1.3.10"
lodash "^4.17.4"
quill "^1.3.7"
react-redux@^8.0.5:
version "8.0.7"
resolved "https://registry.npmmirror.com/react-redux/-/react-redux-8.0.7.tgz"
......@@ -2835,6 +2938,13 @@ react-router@6.11.1:
dependencies:
"@remix-run/router" "1.6.1"
react-viewer@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/react-viewer/-/react-viewer-3.2.2.tgz#38626a7fa862ec84dfa373c2ef5843f4100c465b"
integrity sha512-DHOq1x6cXsAViY43204ILRzLVR5ovP1MgzsC+LzZCWlInRuHjzAgpQZ8GzWm1CkiNYuHGwCxH36X0JUHl2xDSg==
dependencies:
classnames "^2.2.5"
react@^18.1.0:
version "18.1.0"
resolved "https://registry.npmjs.org/react/-/react-18.1.0.tgz"
......@@ -2881,6 +2991,15 @@ regenerator-runtime@^0.13.4:
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
regexp.prototype.flags@^1.2.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb"
integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==
dependencies:
call-bind "^1.0.2"
define-properties "^1.2.0"
functions-have-names "^1.2.3"
regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3:
version "1.4.3"
resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz"
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论