提交 3f1f71dc 作者: 18928357778

添-活动管理-活动列表,优惠券管理-活动优惠券,裂变优惠券,优惠券明细

上级 db0faf09
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -18,11 +18,13 @@ ...@@ -18,11 +18,13 @@
"antd": "^5.4.2", "antd": "^5.4.2",
"axios": "^1.4.0", "axios": "^1.4.0",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"file-saver": "^2.0.5",
"js-base64": "^3.7.3", "js-base64": "^3.7.3",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"match-sorter": "^6.3.1", "match-sorter": "^6.3.1",
"moment": "^2.29.4",
"pinyin-pro": "^3.14.0", "pinyin-pro": "^3.14.0",
"qs": "^6.10.3", "qs": "^6.10.3",
"query-string": "^8.1.0", "query-string": "^8.1.0",
...@@ -34,6 +36,7 @@ ...@@ -34,6 +36,7 @@
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@types/file-saver": "^2.0.5",
"@types/js-cookie": "^3.0.1", "@types/js-cookie": "^3.0.1",
"@types/lodash": "^4.14.182", "@types/lodash": "^4.14.182",
"@types/node": "^20.1.3", "@types/node": "^20.1.3",
......
import { PointManageAPI } from './modules/pointManageAPI'; import { PointManageAPI } from './modules/pointManageAPI';
import { ActivityManageAPI } from './modules/activityManage';
import { CouponManageAPI } from './modules/couponManage'
import { CommonAPI } from "./modules/common";
import { MakeManageAPI } from './modules/makeManage'
export { PointManageAPI }; export {
PointManageAPI,
ActivityManageAPI,
CommonAPI,
CouponManageAPI,
MakeManageAPI
};
// 活动-裂变优惠券下拉类型
import { InterFunction, InterItemFunction } from "~/api/interface";
import { SplitCouponItemType } from "~/api/interface/couponManage";
// 裂变优惠券-下拉
export type splitCouponDownType = InterFunction<
{ type: number },
SplitCouponItemType[]
>;
// 活动新增-type
export type addActivityItemType = {
activityImg: string;
activityName: string;
bindingCouponId?: number;
activityType: number;
endTime: string;
helpType: number;
limitNum: number;
shareNum: number;
show: number;
startTime: string;
time?: any[];
redirectPath?: "";
};
export type addActivityType = InterFunction<addActivityItemType, null>;
// 活动-列表
export type activityItemType = addActivityItemType & {
bindingCouponName: string;
couponActivityDTO: SplitCouponItemType;
activityStatus: number;
activityType: number;
id: number;
};
export type activeTableItemType = InterItemFunction<any, activityItemType[]>;
// 活动-结束
export type endActivityType = InterFunction<{ activityId: number }, null>;
// 活动-数据
export type activityDataType = InterFunction<
{ activityId: number },
{ countFinishParticipate: number; countParticipate: number }
>;
// 活动-编辑
export type activityEditType = InterFunction<
addActivityItemType & { id: number },
null
>;
import {
InterFunction,
InterItemFunction,
InterListFunction,
} from "~/api/interface";
import { PaginationEntity } from "~/common/interface/PaginationEntity";
// 裂变优惠券-表格数据类型
export type SplitCouponItemType = {
id: number;
activityRole: string;
couponStatus: boolean;
amountReceived: number;
userTag: number;
peopleNumber: number;
beSharedCoupon: SplitCouponItemType;
isFixedTime?: number;
} & shareCouponType &
baseInfoType;
export type SplitCouponSearchType = {
couponId?: string;
couponName?: string;
endTime?: string;
startTime?: string;
useType?: number;
state?: boolean;
};
export type SplitCouponListType = InterItemFunction<
SplitCouponSearchType,
SplitCouponItemType[]
>;
// 裂变优惠券-新建-数据类型
// 基本信息
export type baseInfoType = {
couponName: string;
};
// 分享者优惠券
export type shareCouponType = {
useType: number;
primaryKey: number[];
useStartTime: string;
useEndTime: string;
couponMoney: number;
couponType: number;
minPrice: number;
couponDiscount: number;
verificationType: number;
couponTotal: number;
restrictedAccess: number;
userTag: number;
};
// 被分享者优惠券
export type sharedCouponType = {
beSharedCouponDiscount: number;
beSharedCouponMoney: number;
beSharedCouponType: number;
beSharedMinPrice: number;
beSharedPrimaryKey: number[];
beSharedUseType: number;
beSharedVerificationType: number;
beSharedCouponTotal: number;
};
export type SplitCouponAddType = InterItemFunction<
baseInfoType & shareCouponType & sharedCouponType,
null
>;
// 优惠券列表-分页
export type CouponPageListType = InterListFunction<
{
couponId?: string;
couponName?: string;
endTime?: string;
startTime?: string;
state?: boolean;
useType?: number;
},
{
file: any;
couponDay: number;
couponDiscount: number;
couponMoney: number;
couponName: string;
couponStatus: boolean;
couponTotal: number;
couponType: number;
createTime: string;
getType: number;
id: number;
isDel: boolean;
isFixedTime: number;
isLimited: boolean;
lastTotal: number;
minPrice: number;
preferentialLimit: number;
primaryKey: string;
restrictedAccess: number;
updateTime: string;
useEndTime: string;
useStartTime: string;
useType: number;
userTag: number;
verificationType: boolean;
fileUrl: string;
}
>;
// 优惠券明细
export type couponDetailSearchType = {
couponId?: number;
createTime?: string;
endTime?: string;
gainType?: string;
orderNumber?: string;
startTime?: string;
state?: number;
uid?: string;
useTime?: string;
userPhone?: string;
};
export type couponDetailItemType = {
cid: number;
couponId: number;
couponName: string;
uid: string;
userPhone: string;
gainType: string;
status: number;
useTime: string;
orderNo: string;
useType: number;
createTime: string;
conversionRatio: number;
transferorUid: string;
receiveUid: string;
transferorTime: string;
};
export type couponDetailType = InterItemFunction<
couponDetailSearchType,
couponDetailItemType[]
>;
export type couponDetailExportType = InterFunction<
couponDetailSearchType & Pick<PaginationEntity, "pageNo" | "pageSize">,
any
>;
// 品牌列表
export type ListBrandInfoType = InterListFunction<
{},
{ brandName: string; createTime: string; id: number }
>;
// 新增优惠券
export type CouponSaveType = InterFunction<any, any>;
// 增发优惠券
export type CouponIncreaseType = InterFunction<
{ id: number; count: number },
{}
>;
// 获取优惠券使用数据
export type CouponGetDataType = InterFunction<
{ id: number },
{
accountPaid: number;
availability: string;
claimRate: string;
couponTotal: number;
receiveQuantity: number;
usageAmount: number;
}
>;
// 关闭优惠券类型
export type CouponShutDownType = InterFunction<{ id: number }, {}>;
import { InterFunction, InterItemFunction } from "~/api/interface";
// 品牌管理-新增
export type MakeAddType = InterFunction<{ brandName: string }, null>;
// 品牌编辑
export type MakeEditType = InterFunction<{ brandName: string; id: any }, null>;
// 品牌管理-列表
export type MakeItemEntity = {
createTime: string;
brandName: string;
id: number;
};
export type MakeListType = InterItemFunction<{}, MakeItemEntity[]>;
// 品牌刪除
export type MakeDeleteType = InterFunction<{ id: number }, null>;
import {
activeTableItemType,
activityDataType,
activityEditType,
addActivityType,
endActivityType,
splitCouponDownType,
} from "../interface/activityManage";
import axios from "../request";
export class ActivityManageAPI {
// 活动-裂变优惠券-下拉
static getActivityCouponPullDown: splitCouponDownType = (params) => {
return axios.get("userpay/coupon/back/getActivityCouponPullDown", {
params,
});
};
// 活动-新增
static createActivity: addActivityType = (data) => {
return axios.post("malluser/activity/create", data);
};
// 活动-编辑
static editActivity: activityEditType = (data) => {
return axios.post("malluser/activity/update", data);
};
// 活动-列表
static getActivityList: activeTableItemType = (params) => {
return axios.get("malluser/activity/list", { params });
};
// 活动-结束
static endActivity: endActivityType = (params) => {
return axios.get("malluser/activity/stop", { params });
};
// 活动-数据
static getActivityData: activityDataType = (params) => {
return axios.get("malluser/activity/dataInfo", { params });
};
}
import axios from "../request";
// 运营人员
export interface operateEntity {
userName: string;
accountNo: string;
id: number;
uid: string;
}
export interface loginEntity {
accountNo: string;
passWord: string;
remember: boolean;
}
export class CommonAPI {
static Login = (data: Pick<loginEntity, "accountNo" | "passWord">): any => {
return axios.post("userservlet/auth/platformLogin", {
...data,
portType: 1,
});
};
// 上传图片
static commonUpload = (data: FormData) => {
return axios.post("ossservlet/upload/osses", data);
};
// 上传图片
static fileUpload = (data: FormData) => {
return axios.post("ossservlet/upload/oss", data);
};
// 运营人员列表
static operateList(operateName: string) {
return axios.post("orderservlet/ordertask/listOperate", { operateName });
}
// 销售人员列表
static getSaleList() {
return axios.get("userservlet/useraccount/getSaleList");
}
}
import axios from "../request";
import {
couponDetailExportType,
couponDetailType,
CouponGetDataType,
CouponIncreaseType,
CouponPageListType,
CouponSaveType,
CouponShutDownType,
ListBrandInfoType,
SplitCouponAddType,
SplitCouponListType,
} from "~/api/interface/couponManage";
export class CouponManageAPI {
// 优惠券管理-裂变优惠券列表
static getPageActivityList: SplitCouponListType = (data) => {
return axios.post("userpay/coupon/back/pageActivityList", data);
};
// 优惠券管理-裂变优惠券新建
static saveActivity: SplitCouponAddType = (data) => {
return axios.post("userpay/coupon/back/saveActivity", data);
};
// 优惠券管理-裂变优惠券新建
static CouponPageList: CouponPageListType = (data) => {
return axios.post("userpay/coupon/back/pageList", data);
};
// 优惠券明细
static getCouponDetail: couponDetailType = (data) => {
return axios.post("userpay/coupon/back/getUserCouponList", data);
};
// 优惠券明细-导出
static downloadCouponUserList: couponDetailExportType = (data) => {
return axios.post("userpay/coupon/back/downloadCouponUserList", data, {
responseType: "blob",
});
};
// 新增优惠券
static CouponSave: CouponSaveType = (data) => {
return axios.post("/userpay/coupon/back/save", data, {
headers: { "Content-Type": "multipart/form-data" },
});
};
// 增发优惠券
static CouponIncrease: CouponIncreaseType = (params) => {
return axios.post("/userpay/coupon/back/increase", {}, { params });
};
// 获取优惠券详情
static CouponGetData: CouponGetDataType = (params) => {
return axios.get("/userpay/coupon/back/getData", { params });
};
// 关闭优惠券
static CouponShutDown: CouponShutDownType = (params) => {
return axios.post("/userpay/coupon/back/shutDown", {}, { params });
};
}
import {
MakeAddType,
MakeDeleteType,
MakeEditType,
MakeListType,
} from "~/api/interface/makeManage";
import axios from "../request";
export class MakeManageAPI {
// 品牌-新增
static addBrandInfo: MakeAddType = (params) => {
return axios.get("uavgoods/brand/addBrandInfo", { params });
};
// 品牌-编辑
static editBrandInfo: MakeEditType = (params) => {
return axios.get("uavgoods/brand/editBrandInfo", { params });
};
// 品牌-列表
static getListBrandInfo: MakeListType = (params) => {
return axios.get("uavgoods/brand/listBrandInfo", { params });
};
// 品牌刪除
static deleteBrandInfo: MakeDeleteType = (params) => {
return axios.get("uavgoods/brand/deleteBrandInfo", { params });
};
}
...@@ -4,7 +4,7 @@ import { limitEntity } from "@/api/modules/role"; ...@@ -4,7 +4,7 @@ import { limitEntity } from "@/api/modules/role";
function useOptionShow(id: number) { function useOptionShow(id: number) {
// const [show, setShow] = useState<boolean>(false); // const [show, setShow] = useState<boolean>(false);
return JSON.parse(localStorage.getItem("routeList") as string).some( return JSON.parse(localStorage.getItem("routeList") as string)?.some(
(v: limitEntity) => v.id === id (v: limitEntity) => v.id === id
); );
// useEffect(() => { // useEffect(() => {
......
/*
* @Author: 龚洪江
* @Date: 2022-07-26 11:43:44
* @LastEditors: 龚洪江
* @LastEditTime: 2022-07-26 11:45:29
* @FilePath: \code\mmc-store\src\common\PaginationEntity\index.ts
* @Description:
*
* Copyright (c) 2022 by 龚洪江 2238959530@qq.com, All Rights Reserved.
*/
export interface PaginationEntity {
pageNo: number;
pageSize: number;
totalCount: number;
}
export interface PropsType {
title?: string;
isModalVisible: boolean;
handleOk: Function;
handleCancel: Function;
}
.box-wrap {
background-color: #fff;
border-radius: 10px;
margin-bottom: 15px;
min-height: 50px;
padding: 10px 20px 0 20px;
box-sizing: border-box;
.ant-form-item {
margin-bottom: 10px;
}
}
import React from "react";
import "./index.scss";
interface PropsType {
children: React.ReactNode;
}
const Box: React.FC<PropsType> = ({ children }) => {
return <div className="box-wrap">{children}</div>;
};
export default Box;
import React from "react";
interface PropsType {
children: React.ReactNode;
}
export const TableBox: React.FC<PropsType> = (props) => {
const { children } = props;
return <div className="table-body-box">{children}</div>;
};
import { Modal, Table } from "antd";
import { FC } from "react";
import { ColumnsType } from "antd/es/table";
import { PropsType } from "~/common/interface/modal";
import { activityDataType } from "~/api/interface/activityManage";
// 活动数据-类型
type dataType = (ReturnType<activityDataType> extends Promise<infer T>
? T
: never)["result"];
interface selfProps {
activityData: dataType | undefined;
}
const ActivityDataModal: FC<
Pick<PropsType, "isModalVisible" | "handleCancel"> & selfProps
> = ({ isModalVisible, handleCancel, activityData }) => {
const columns: ColumnsType<dataType> = [
{
title: "参与总人数",
dataIndex: "countFinishParticipate",
align: "center",
},
{
title: "参与成功人数",
dataIndex: "countParticipate",
align: "center",
},
];
return (
<Modal
open={isModalVisible}
title="活动数据"
onCancel={() => handleCancel()}
footer={null}
>
{activityData && (
<Table
columns={columns}
pagination={false}
dataSource={[activityData]}
bordered
rowKey="countFinishParticipate"
/>
)}
</Modal>
);
};
export default ActivityDataModal;
import {
FC,
ForwardedRef,
forwardRef,
useState,
useImperativeHandle,
} from "react";
import {
DatePicker,
Form,
Input,
message,
Modal,
Radio,
Select,
Upload,
} from "antd";
import { PlusOutlined } from "@ant-design/icons";
import type { UploadFile } from "antd/es/upload/interface";
import dayjs from "dayjs";
import { RangePickerProps } from "antd/es/date-picker";
import { PropsType } from "~/common/interface/modal";
import { CommonAPI } from "~/api";
import { SplitCouponItemType } from "~/api/interface/couponManage";
import { addActivityItemType } from "~/api/interface/activityManage";
import {ActivityManageAPI} from "~/api/modules/activityManage";
interface selfProps {
couponData: SplitCouponItemType[];
activityTypeChangeEvent: (value: number) => void;
ref: ForwardedRef<any>;
}
const AddOrEditModal: FC<PropsType & selfProps> = forwardRef(
(
{
isModalVisible,
handleOk,
handleCancel,
couponData,
activityTypeChangeEvent,
title,
},
ref
) => {
const [createActivityForm] = Form.useForm<
Exclude<
addActivityItemType,
{
startTime: string;
endTime: string;
helpType: number;
shareNum: number;
}
>
>();
const [activityId, setActivityId] = useState<number>(-1);
const [currentActivityType, setCurrentActivityType] = useState<number>(1);
// 文件
const [fileList, setFileList] = useState<UploadFile[]>([]);
const handleOkEvent = () => {
createActivityForm.validateFields().then((value: any) => {
const splitCouponItem = couponData.find(
(v) => v.id === value.bindingCouponId
);
const startTime = `${dayjs(value.time[0]).format(
"YYYY-MM-DD"
)} 00:00:00`;
const endTime = `${dayjs(value.time[1]).format("YYYY-MM-DD")} 23:59:59`;
delete value.time;
ActivityManageAPI[
activityId === -1 ? "createActivity" : "editActivity"
]({
id: activityId === -1 ? undefined : activityId,
...value,
limitNum: Number(value.limitNum),
helpType:
splitCouponItem?.beSharedCoupon?.userTag ||
splitCouponItem?.userTag,
shareNum:
currentActivityType === 1
? splitCouponItem?.peopleNumber
: undefined,
startTime,
endTime,
}).then(({ code }) => {
if (code === "200") {
message.success(activityId ? "编辑成功" : "新增成功");
createActivityForm.resetFields();
setFileList([]);
handleOk();
}
});
});
};
const handleCancelEvent = () => {
handleCancel();
createActivityForm.resetFields();
setFileList([]);
};
// 上传
const beforeUpload = (file: UploadFile) => {
const reg = /\/(png|jpg|gif|jpeg|webp)$/;
const isType = reg.test(file.type as string);
if (!isType) {
message.warning("文件格式错误");
}
return isType;
};
const customRequest = (value: any) => {
const formData = new FormData();
formData.append("uploadFile", value.file);
CommonAPI.commonUpload(formData).then((res: any) => {
if (res.code === "200") {
setFileList([
{
uid: "uid",
name: "image",
url: res.result[0],
status: "done",
},
]);
createActivityForm.setFieldValue("activityImg", res.result[0]);
}
});
};
const onRemove = (file: UploadFile) => {
const list = fileList.filter((v: UploadFile) => v.uid !== file.uid);
setFileList(list);
setTimeout(() => {
createActivityForm.setFieldValue("activityImg", undefined);
});
};
// 活动类型改变
const activityTypeChange = (e: any) => {
createActivityForm.setFieldValue("bindingCouponId", undefined);
setCurrentActivityType(e.target.value);
activityTypeChangeEvent(e.target.value);
};
// 正则
const negativeNumberValidator = (
rule: any,
value: string,
callback: (error?: string) => void
) => {
if (!value) {
return Promise.reject(new Error(`请输入参与次数!`));
}
const reg = /^\+?[1-9][0-9]*$/;
if (!reg.test(value)) {
return Promise.reject(new Error(`参与次数为非零的正整数!`));
}
if (Number(value) > 5) {
return Promise.reject(new Error("参与次数最大值为5!"));
}
// const bindingCouponId =
// createActivityForm.getFieldValue("bindingCouponId");
// if (bindingCouponId) {
// const splitCouponObj = couponData.find(
// (v) => v.id === bindingCouponId
// );
// if (splitCouponObj && Number(value) > splitCouponObj.restrictedAccess) {
// return Promise.reject("参与次数不能大于关联优惠券每人限领张数");
// }
// }
return Promise.resolve();
};
// rangPicker disabledDate
const disabledDate: RangePickerProps["disabledDate"] = (currentDate) => {
// const bindingCouponId = createActivityForm.getFieldValue("bindingCouponId");
// const couponObj = couponData.find((v) => v.id === bindingCouponId);
// const activityType = createActivityForm.getFieldValue("activityType");
// if (bindingCouponId) {
// switch (activityType) {
// case 1:
// return !(
// currentDate.isBefore(couponObj?.useEndTime) &&
// currentDate.isAfter(couponObj?.useStartTime)
// );
// case 2:
// return (
// couponObj?.isFixedTime === 0 &&
// !(
// currentDate.isBefore(couponObj?.useEndTime) &&
// currentDate.isAfter(couponObj?.useStartTime)
// )
// );
// }
// }
return currentDate < dayjs().subtract(1, "day");
};
useImperativeHandle(ref, () => ({
getForm: () => createActivityForm,
setFileList: (fileList: UploadFile[]) => {
setFileList(fileList);
},
setId: (id: number) => {
setActivityId(id);
},
}));
return (
<Modal
open={isModalVisible}
onOk={handleOkEvent}
onCancel={handleCancelEvent}
title={title}
>
<Form
labelCol={{ span: 5 }}
wrapperCol={{ span: 14 }}
form={createActivityForm}
initialValues={{ show: 1, activityType: 1 }}
>
<Form.Item
label="活动标题"
name="activityName"
rules={[{ required: true, message: "请输入活动标题!" }]}
>
<Input placeholder="请输入活动标题" maxLength={25} />
</Form.Item>
<Form.Item label="活动类型" name="activityType">
<Radio.Group onChange={activityTypeChange}>
<Radio value={1}>裂变活动</Radio>
<Radio value={2}>普通活动</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label="活动中心展示" name="show">
<Radio.Group>
<Radio value={1}></Radio>
<Radio value={0}></Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="推广简图"
name="activityImg"
rules={[{ required: true, message: "请上传推广简图!" }]}
>
<Upload
listType="picture-card"
fileList={fileList}
beforeUpload={beforeUpload}
customRequest={customRequest}
onRemove={onRemove}
accept="image/*"
>
{fileList.length < 1 && <PlusOutlined />}
</Upload>
</Form.Item>
<Form.Item label="关联优惠券" name="bindingCouponId">
<Select
placeholder="请选择关联优惠券"
showSearch
filterOption={(input, option: any) =>
(option!.children as unknown as string)
.toLowerCase()
.includes(input.toLowerCase())
}
>
{couponData.map((v) => (
<Select.Option value={v.id} key={v.id}>
{v.couponName}
</Select.Option>
))}
</Select>
</Form.Item>
{currentActivityType === 1 && (
<Form.Item
label="参与次数"
name="limitNum"
rules={[{ required: true, validator: negativeNumberValidator }]}
>
<Input placeholder="请输入参与次数" suffix="次" />
</Form.Item>
)}
<Form.Item
label="生效时间"
rules={[{ required: true, message: "请选择生效时间!" }]}
name="time"
>
<DatePicker.RangePicker disabledDate={disabledDate} />
</Form.Item>
</Form>
</Modal>
);
}
);
export default AddOrEditModal;
import { FC } from "react";
import { Modal, Descriptions } from "antd";
import { PropsType } from "~/common/interface/modal";
import { couponDetailItemType } from "~/api/interface/couponManage";
const contentStyle = { width: "100px" };
const DataModal: FC<
Pick<PropsType, "isModalVisible" | "handleCancel"> & {
couponDetailItem: couponDetailItemType | undefined;
}
> = ({ isModalVisible, handleCancel, couponDetailItem }) => {
return (
<Modal
open={isModalVisible}
title="查看详情"
footer={null}
onCancel={() => handleCancel()}
width={650}
>
<Descriptions bordered size="small">
<Descriptions.Item label="兑换时间" contentStyle={contentStyle}>
{couponDetailItem?.createTime}
</Descriptions.Item>
<Descriptions.Item label="兑换比例" contentStyle={contentStyle}>
{couponDetailItem?.conversionRatio}
</Descriptions.Item>
{(couponDetailItem?.gainType === "presented" ||
couponDetailItem?.gainType === "acquire") && (
<>
<Descriptions.Item label="转赠人UID" contentStyle={contentStyle}>
{couponDetailItem?.transferorUid}
</Descriptions.Item>
<Descriptions.Item label="获赠人UID" contentStyle={contentStyle}>
{couponDetailItem?.receiveUid}
</Descriptions.Item>
<Descriptions.Item label="转赠时间" contentStyle={contentStyle}>
{couponDetailItem?.transferorTime}
</Descriptions.Item>
<Descriptions.Item label="获赠时间" contentStyle={contentStyle}>
{couponDetailItem?.createTime}
</Descriptions.Item>
</>
)}
</Descriptions>
</Modal>
);
};
export default DataModal;
import React, { useState } from "react";
import { DatePicker, Form, Input, message, Modal, Radio } from "antd";
import moment from "moment";
// 传参类型
interface propType {
title: string;
open: boolean;
closed: any;
data?: any;
}
export const AddEditModal: React.FC<propType> = (props: propType) => {
// 组件默认值
AddEditModal.defaultProps = {
data: null,
};
// 参数
const { title, open, closed, data } = props;
// 表单钩子
const [form] = Form.useForm();
// 生效时间单选
const [radioValue, setRadioValue] = useState(0);
// 关闭弹窗
const handleCancel = () => {
closed();
};
// 提交弹窗
const handleOk = () => {
form
.validateFields()
.then((values) => {
console.log("handleOk -->", values);
})
.catch((err) => {
message
.warning({
content: err.errorFields[0].errors[0],
})
.then();
});
};
return (
<Modal
open={open}
title={title}
onCancel={handleCancel}
onOk={handleOk}
destroyOnClose
>
<Form
name="addForm"
form={form}
labelAlign="right"
// layout="inline"
>
<Form.Item
label="规则名称"
name="ruleName"
rules={[{ required: true, message: "请输入规则名称" }]}
>
<Input placeholder="请输入规则名称" maxLength={20} allowClear />
</Form.Item>
<Form.Item label="兑换比例(积分:券额)" required>
<Form.Item
name="year"
rules={[{ required: true, message: "请输入积分比例" }]}
style={{ display: "inline-block", width: "calc(50% - 8px)" }}
>
<Input placeholder="请输入积分比例" />
</Form.Item>
<Form.Item
name="month"
rules={[{ required: true, message: "请输入券额比例" }]}
style={{
display: "inline-block",
width: "calc(50% - 8px)",
margin: "0 8px",
}}
>
<Input placeholder="请输入券额比例" />
</Form.Item>
</Form.Item>
<div style={{ transform: "translateY(-10px)" }}>
说明:若兑换比例为1:20,则1积分可兑20元VIP优惠券,且为无门槛优惠券
</div>
<Form.Item
label="生效时间"
name="ruleSetting"
rules={[{ required: true, message: "请选择生效时间" }]}
initialValue={0}
>
<Radio.Group
options={[
{ label: "立即生效", value: 0 },
{ label: "手动设置", value: 1 },
]}
value={radioValue}
onChange={({ target: { value } }) => {
setRadioValue(value);
}}
/>
</Form.Item>
{radioValue === 1 && (
<Form.Item
label="手动设置"
name="ruleTime"
rules={[{ required: radioValue === 1, message: "请设置生效时间" }]}
>
<DatePicker
placeholder="请输入账号有效期"
allowClear
showTime={{ format: "HH:mm:ss" }}
format="YYYY-MM-DD HH:mm:ss"
style={{ width: "100%" }}
disabledDate={(current) => {
// 限制时间不可早于当日
return current && current <= moment();
}}
/>
</Form.Item>
)}
</Form>
</Modal>
);
};
import React, { useEffect, useState } from "react";
import { Descriptions, message, Modal } from "antd";
import { CouponManageAPI } from "~/api";
import { CouponGetDataType } from "~/api/interface/couponManage";
// 传参类型
interface propType {
title: string;
open: boolean;
closed: any;
data?: any;
}
// 列表的类型
type DetailType = (ReturnType<CouponGetDataType> extends Promise<infer T>
? T
: never)["result"];
const contentStyle = { width: "100px" };
export const DataModal: React.FC<propType> = (props: propType) => {
// 组件默认值
DataModal.defaultProps = {
data: null,
};
// 参数
const { title, open, closed, data } = props;
// 使用数据
const [couponDetail, setCouponDetail] = useState<DetailType>();
// 关闭弹窗
const handleCancel = () => {
closed();
};
// 获取优惠券使用数据
const getCouponGetData = async () => {
const res = await CouponManageAPI.CouponGetData({
id: data.id,
});
if (res && res.code === "200") {
setCouponDetail(res.result);
}
};
// 组件加载
useEffect(() => {
if (!data) return;
getCouponGetData().then();
}, [open]);
return (
<Modal
open={open}
title={title}
onCancel={handleCancel}
footer={null}
destroyOnClose
width={650}
>
<Descriptions column={3} bordered size="small">
<Descriptions.Item contentStyle={contentStyle} label="总发行量">
{couponDetail?.couponTotal}
</Descriptions.Item>
<Descriptions.Item contentStyle={contentStyle} label="领取量">
{couponDetail?.receiveQuantity}
</Descriptions.Item>
<Descriptions.Item contentStyle={contentStyle} label="领取率">
{couponDetail?.claimRate}
</Descriptions.Item>
<Descriptions.Item contentStyle={contentStyle} label="使用量">
{couponDetail?.usageAmount}
</Descriptions.Item>
<Descriptions.Item contentStyle={contentStyle} label="有效使用量">
{couponDetail?.accountPaid}
</Descriptions.Item>
<Descriptions.Item contentStyle={contentStyle} label="有效使用率">
{couponDetail?.availability}
</Descriptions.Item>
</Descriptions>
</Modal>
);
};
import React, { useEffect } from "react";
import { Form, Input, message, Modal } from "antd";
import { maxLength } from "~/utils/validateUtils";
import { CouponManageAPI } from "~/api";
// 传参类型
interface propType {
title: string;
open: boolean;
closed: any;
data?: any;
}
export const IncreaseModal: React.FC<propType> = (props: propType) => {
// 组件默认值
IncreaseModal.defaultProps = {
data: null,
};
// 参数
const { title, open, closed, data } = props;
// 表单钩子
const [form] = Form.useForm();
// 关闭弹窗
const handleCancel = () => {
form.resetFields();
closed();
};
// 提交弹窗
const handleOk = () => {
form
.validateFields()
.then(async (values) => {
await handleSubmit(values);
// console.log("handleOk -->", values);
})
.catch((err) => {
message
.warning({
content: err.errorFields[0].errors[0],
})
.then();
});
};
// 新增发行量监听
const handleIncrease = ({ target: { value } }: any) => {
if (value) {
form.setFieldsValue({
lastTotalBefore: Number(data.lastTotal) + Number(value),
});
} else {
form.setFieldsValue({
lastTotalBefore: data.lastTotal,
});
}
};
// 提交数据
const handleSubmit = async (values: { count: number }) => {
const res = await CouponManageAPI.CouponIncrease({
id: data.id,
count: Number(values.count),
});
if (res && res.code === "200") {
message.success("操作成功").then();
handleCancel();
}
};
useEffect(() => {
if (!data) return;
// console.log("IncreaseModal --->", data);
form.setFieldsValue({
...data,
lastTotalBefore: data.lastTotal,
});
}, [data]);
return (
<Modal
open={open}
title={title}
onCancel={handleCancel}
onOk={handleOk}
destroyOnClose
width={400}
>
<Form
name="addForm"
form={form}
labelAlign="right"
labelCol={{ span: 8 }}
// layout="inline"
>
<Form.Item label="当前发行总量" name="lastTotal">
<Input
placeholder="请输入当前发行总量"
maxLength={20}
allowClear
disabled
suffix="张"
/>
</Form.Item>
<Form.Item
label="新增发行量"
name="count"
rules={[
{ required: true, message: "请输入新增发行量" },
// 增发合计不能超过100000
{
validator: (rule, value) => {
if (Number(data.lastTotal) + Number(value) > 100000) {
return Promise.reject("增发合计不能超过100000");
}
return Promise.resolve();
},
},
]}
>
<Input
placeholder="请输入新增发行量"
maxLength={20}
allowClear
prefix="+"
suffix="张"
type="number"
onChange={handleIncrease}
onInput={maxLength}
/>
</Form.Item>
<Form.Item label="更新后总发行量" name="lastTotalBefore">
<Input
placeholder="请输入更新后总发行量"
maxLength={20}
allowClear
disabled
suffix="张"
/>
</Form.Item>
</Form>
</Modal>
);
};
import { FC, forwardRef, useImperativeHandle } from "react";
import "./index.scss";
import { Form, Input } from "antd";
import { baseInfoType } from "~/api/interface/couponManage";
interface selfPops {
ref: any;
isDetail: boolean;
}
const BaseInfo: FC<selfPops> = forwardRef(({ isDetail }, ref) => {
const [baseInfoForm] = Form.useForm<baseInfoType>();
useImperativeHandle(ref, () => ({
getForm: () => baseInfoForm,
}));
return (
<div className="split-coupon-base-info">
<div className="title">基本信息</div>
<div className="base-info-form">
<Form
labelCol={{ span: 2 }}
wrapperCol={{ span: 5 }}
form={baseInfoForm}
disabled={isDetail}
>
<Form.Item
label="优惠券名称"
name="couponName"
rules={[{ required: true, message: "请输入优惠券名称" }]}
>
<Input placeholder="请输入优惠券名称" maxLength={10} />
</Form.Item>
</Form>
</div>
</div>
);
});
export default BaseInfo;
.add-or-edit-or-detail-wrap{
min-width: 1200px;
height: 100%;
position: relative;
.add-or-edit-or-detail{
height: calc(100% - 50px);
overflow-y: auto;
.split-coupon-base-info,.share-coupon-info,.shared-coupon-info{
padding: 10px;
background-color: #fff;
margin-bottom: 20px;
.title{
font-size: 20px;
font-weight: bold;
color: #000;
margin-bottom: 10px;
}
}
.ant-form-item{
margin-bottom: 10px;
}
}
.footer-operate{
position: absolute;
bottom: -15px;
left: -10px;
height: 80px;
width: calc(100% + 20px) ;
background-color: #fff;
box-shadow: 0 -1px 2px 0 rgba(0,0,0,0.1);
display: flex;
align-items: center;
justify-content: center;
button{
width: 100px;
}
button:first-child{
margin-right: 150px;
}
}
}
import { FC, useEffect, useRef, useState } from "react";
import "./index.scss";
import { Button, message } from "antd";
import { useNavigate } from "react-router-dom";
import dayJs from "dayjs";
import qs from "query-string";
import BaseInfo from "./components/baseInfo";
import ShareCouponInfo from "./components/shareCouponInfo";
import SharedCouponInfo from "./components/sharedCouponInfo";
import { MakeManageAPI } from "~/api/modules/makeManage";
import { MakeItemEntity } from "~/api/interface/makeManage";
import { CouponManageAPI } from "~/api";
const SplitCouponOperate: FC<any> = (props) => {
const history = useNavigate();
const baseInfoRef = useRef<any>();
const shareCouponRef = useRef<any>();
const sharedCouponRef = useRef<any>();
// 品牌列表数据
const [makeList, setMakeList] = useState<MakeItemEntity[]>([]);
// 品牌-列表
const getMakeList = () => {
MakeManageAPI.getListBrandInfo({ pageNo: 1, pageSize: 999999 }).then(
({ result }) => {
setMakeList(result.list || []);
}
);
};
const [splitCouponId, setSplitCouponId] = useState<string>("");
// 详情
const getSplitCouponDetail = (couponId: string) => {
CouponManageAPI.getPageActivityList({
couponId,
pageNo: 1,
pageSize: 10,
useType: 2,
}).then(({ result }) => {
if (result.list[0]) {
(shareCouponRef.current as any).setCouponType(
result.list[0].couponType
);
const { beSharedCoupon } = result.list[0];
(sharedCouponRef.current as any).setCouponType(
beSharedCoupon.couponType
);
(baseInfoRef.current as any).getForm().setFieldsValue({
couponName: result.list[0].couponName,
});
(shareCouponRef.current as any).getForm().setFieldsValue({
primaryKey: (result.list[0].primaryKey as any).split(",").map(Number),
time: [
dayJs(result.list[0].useStartTime),
dayJs(result.list[0].useEndTime),
],
couponMoney: result.list[0].couponMoney,
couponType: result.list[0].couponType,
minPrice: result.list[0].minPrice || undefined,
couponDiscount: result.list[0].couponDiscount || undefined,
verificationType: result.list[0].verificationType ? 1 : 0,
couponTotal: result.list[0].couponTotal,
restrictedAccess: result.list[0].restrictedAccess,
peopleNumber: result.list[0].peopleNumber,
userTag: beSharedCoupon.userTag || undefined,
});
(sharedCouponRef.current as any).getForm().setFieldsValue({
beSharedPrimaryKey: (beSharedCoupon.primaryKey as any)
.split(",")
.map(Number),
beSharedCouponMoney: beSharedCoupon.couponMoney,
beSharedCouponType: beSharedCoupon.couponType,
beSharedMinPrice: beSharedCoupon.minPrice || undefined,
beSharedCouponDiscount: beSharedCoupon.couponDiscount || undefined,
beSharedVerificationType: beSharedCoupon.verificationType ? 1 : 0,
beSharedCouponTotal: beSharedCoupon.couponTotal,
});
}
});
};
const addSplitCouponSubmit = () => {
const { getForm: getBaseInfoForm } = baseInfoRef.current;
const { getForm: getShareCouponForm } = shareCouponRef.current;
const { getForm: getSharedCouponForm } = sharedCouponRef.current;
Promise.all([
getBaseInfoForm().validateFields(),
getShareCouponForm().validateFields(),
getSharedCouponForm().validateFields(),
])
.then((values) => {
values[1].useStartTime = `${dayJs(values[1].time[0]).format(
"YYYY-MM-DD"
)} 00:00:00`;
values[1].useEndTime = `${dayJs(values[1].time[1]).format(
"YYYY-MM-DD"
)} 23:59:59`;
values[1].primaryKey = values[1].primaryKey.join(",");
values[2].beSharedPrimaryKey = values[2].beSharedPrimaryKey.join(",");
delete values[1].time;
CouponManageAPI.saveActivity({
...values[0],
...values[1],
...values[2],
}).then(({ code, message: msg }) => {
if (code === "200") {
message.success("新增成功");
getBaseInfoForm().resetFields();
getShareCouponForm().resetFields();
getSharedCouponForm().resetFields();
backRoute();
} else {
message.error(msg);
}
});
})
.catch((error) => {
message.error(error.errorFields[0].errors[0]);
});
};
// 返回
const backRoute = () => {
history(-1);
};
useEffect(() => {
getMakeList();
if (props.location) {
const { id: splitCouponId }: any = qs.parse(props.location.search);
setSplitCouponId(splitCouponId);
if (splitCouponId) {
getSplitCouponDetail(splitCouponId);
}
}
}, []);
return (
<div className="add-or-edit-or-detail-wrap">
<div className="add-or-edit-or-detail">
{/* 基本信息 */}
<BaseInfo ref={baseInfoRef} isDetail={!!splitCouponId} />
{/* 分享者优惠券 */}
<ShareCouponInfo
ref={shareCouponRef}
makeList={makeList}
isDetail={!!splitCouponId}
/>
{/* 被分享者优惠券 */}
<SharedCouponInfo
ref={sharedCouponRef}
makeList={makeList}
shareCouponRef={shareCouponRef}
isDetail={!!splitCouponId}
/>
</div>
<div className="footer-operate">
<Button onClick={backRoute}>返回</Button>
{!splitCouponId && (
<Button type="primary" onClick={addSplitCouponSubmit}>
确定
</Button>
)}
</div>
</div>
);
};
export default SplitCouponOperate;
.split-coupon-list{
.ant-table-thead > tr > th:not(:last-child):not(.ant-table-selection-column):not(.ant-table-row-expand-icon-cell):not([colspan])::before{
width: 0;
}
.ant-table-tbody > tr{
border-bottom: 1px solid #999;
}
}
...@@ -2,7 +2,7 @@ import React from 'react'; ...@@ -2,7 +2,7 @@ import React from 'react';
import { Navigate, RouteObject } from 'react-router-dom'; import { Navigate, RouteObject } from 'react-router-dom';
import ErrorPage from '~/pages/common/error'; import ErrorPage from '~/pages/common/error';
import LayoutView from '~/components/layout'; import LayoutView from '~/components/layout';
import { AccountBookOutlined, MacCommandOutlined } from '@ant-design/icons'; import { AccountBookOutlined, MacCommandOutlined ,GiftOutlined,PayCircleOutlined} from '@ant-design/icons';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
import { AgnosticIndexRouteObject } from '@remix-run/router'; import { AgnosticIndexRouteObject } from '@remix-run/router';
...@@ -15,10 +15,15 @@ import DivideOrder from '~/pages/pointManage/divideOrder'; ...@@ -15,10 +15,15 @@ import DivideOrder from '~/pages/pointManage/divideOrder';
import DivideRules from '~/pages/pointManage/divideRules'; import DivideRules from '~/pages/pointManage/divideRules';
import LoginView from '~/pages/common/login'; import LoginView from '~/pages/common/login';
const ActivityList = React.lazy(() => import('src/pages/activityManage/activityList'));//活动管理
const ProductOrderView = React.lazy(() => import('src/pages/orderManage/productOrder')); //销售订单 const ProductOrderView = React.lazy(() => import('src/pages/orderManage/productOrder')); //销售订单
const EquipmentOrderView = React.lazy(() => import('src/pages/orderManage/equipmentOrder')); //设备订单 const EquipmentOrderView = React.lazy(() => import('src/pages/orderManage/equipmentOrder')); //设备订单
const ServiceOrderView = React.lazy(() => import('src/pages/orderManage/serviceOrder')); //服务订单 const ServiceOrderView = React.lazy(() => import('src/pages/orderManage/serviceOrder')); //服务订单
const CouponList = React.lazy(() => import('src/pages/couponManage/couponList'));//优惠券管理
const CouponDetail = React.lazy(() => import('src/pages/couponManage/couponList/detail'));//优惠券明细
const SplitCouponList = React.lazy(() => import('src/pages/couponManage/splitCouponList'));//裂变优惠券
const SplitCouponOperate = React.lazy(() => import('src/pages/couponManage/splitCouponList/addOrEditOrDetail'));//裂变优惠券操作
const CouponDetailed = React.lazy(() => import('src/pages/couponManage/couponDetailed'));//优惠券明细
export interface RouteObjectType { export interface RouteObjectType {
path: AgnosticIndexRouteObject['path']; path: AgnosticIndexRouteObject['path'];
element: any; element: any;
...@@ -48,6 +53,27 @@ const withLoadingComponent = (comp: JSX.Element) => ( ...@@ -48,6 +53,27 @@ const withLoadingComponent = (comp: JSX.Element) => (
// 路由数组 // 路由数组
export const routerList: Array<RouteObjectType> = [ export const routerList: Array<RouteObjectType> = [
{ {
path: '/activityManage',
element: <LayoutView />,
errorElement: <ErrorPage />,
meta: {
id: 19000,
icon: <GiftOutlined />,
title: '活动管理',
},
children: [
{
path: '/activityManage/activityList',
element: withLoadingComponent(<ActivityList />),
meta: {
id: 19100,
title: '活动列表',
icon: <GiftOutlined />,
},
},
],
},
{
path: '/orderManage', path: '/orderManage',
element: <LayoutView />, element: <LayoutView />,
errorElement: <ErrorPage />, errorElement: <ErrorPage />,
...@@ -107,7 +133,9 @@ export const routerList: Array<RouteObjectType> = [ ...@@ -107,7 +133,9 @@ export const routerList: Array<RouteObjectType> = [
}, },
{ {
path: '/pointManage/pointList/detail', path: '/pointManage/pointList/detail',
element: withLoadingComponent(<PointDetail />), element: withLoadingComponent(<PointDetail location={{
search: ''
}} />),
meta: { meta: {
id: 25100, id: 25100,
title: '个人积分明细', title: '个人积分明细',
...@@ -126,7 +154,9 @@ export const routerList: Array<RouteObjectType> = [ ...@@ -126,7 +154,9 @@ export const routerList: Array<RouteObjectType> = [
}, },
{ {
path: '/pointManage/pointList/list', path: '/pointManage/pointList/list',
element: withLoadingComponent(<PointDetailList />), element: withLoadingComponent(<PointDetailList location={{
search: ''
}} />),
meta: { meta: {
id: 25100, id: 25100,
title: '积分明细', title: '积分明细',
...@@ -155,6 +185,69 @@ export const routerList: Array<RouteObjectType> = [ ...@@ -155,6 +185,69 @@ export const routerList: Array<RouteObjectType> = [
}, },
], ],
}, },
{
path: '/couponManage',
element: <LayoutView />,
errorElement: <ErrorPage />,
meta: {
id: 26000,
icon: <PayCircleOutlined />,
title: '优惠券管理',
},
children: [
{
path: '/couponManage/couponList',
element: withLoadingComponent(<CouponList />),
meta: {
id: 26100,
title: '活动优惠券',
icon: <PayCircleOutlined />,
},
},
{
path: '/couponManage/couponList/detail',
element: withLoadingComponent(<CouponDetail location={{
search: ''
}} />),
meta: {
id: 26100,
title: '活动优惠券操作',
icon: <PayCircleOutlined />,
hidden:true,
selectKey:'/couponManage/couponList'
},
},
{
path: '/couponManage/splitCouponList',
element: withLoadingComponent(<SplitCouponList />),
meta: {
id: 26200,
title: '裂变优惠券',
icon: <PayCircleOutlined />,
},
},
{
path: '/couponManage/addOrEditOrDetail',
element: withLoadingComponent(<SplitCouponOperate />),
meta: {
id: 26200,
title: '裂变优惠券操作',
icon: <PayCircleOutlined />,
hidden:true,
selectKey:'/couponManage/splitCouponList'
},
},
{
path: '/couponManage/couponDetailed',
element: withLoadingComponent(<CouponDetailed />),
meta: {
id: 26300,
title: '优惠券明细',
icon: <PayCircleOutlined />,
},
},
],
},
]; ];
// 路由白名单 // 路由白名单
export const whiteRouterList: Array<RouteObject & RouteObjectType> = [ export const whiteRouterList: Array<RouteObject & RouteObjectType> = [
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论