提交 becb5aad authored 作者: 王鹏飞's avatar 王鹏飞

refactor: 重构优惠券

上级 a44985f9
...@@ -6,8 +6,6 @@ yarn-debug.log* ...@@ -6,8 +6,6 @@ yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
package-lock.json*
package-lock.json
node_modules node_modules
dist dist
...@@ -24,4 +22,3 @@ dist-ssr ...@@ -24,4 +22,3 @@ dist-ssr
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
package-lock.json
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -81,7 +81,7 @@ dd { ...@@ -81,7 +81,7 @@ dd {
.ant-input-number .ant-input-number-input { .ant-input-number .ant-input-number-input {
text-align: center; text-align: center;
} }
.ant-table-wrapper { .ant-table {
border: 1px solid #e5e5e5; border: 1px solid #e5e5e5;
} }
.ant-form-inline .ant-form-item { .ant-form-inline .ant-form-item {
......
...@@ -117,7 +117,7 @@ const AppList = forwardRef((props, ref) => { ...@@ -117,7 +117,7 @@ const AppList = forwardRef((props, ref) => {
{filters.length > 0 && filterElement} {filters.length > 0 && filterElement}
<div className='app-list-table'> <div className='app-list-table'>
<Table <Table
bordered rowKey='id'
dataSource={data.list} dataSource={data.list}
loading={loading} loading={loading}
pagination={pagination} pagination={pagination}
......
...@@ -6,7 +6,7 @@ import { Provider } from 'react-redux'; ...@@ -6,7 +6,7 @@ import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react'; import { PersistGate } from 'redux-persist/integration/react';
import store, { persistor } from '@/store'; import store, { persistor } from '@/store';
import { ConfigProvider } from 'antd'; import { ConfigProvider, App } from 'antd';
import zhCN from 'antd/locale/zh_CN'; import zhCN from 'antd/locale/zh_CN';
// import 'dayjs/locale/zh-cn'; // import 'dayjs/locale/zh-cn';
...@@ -14,7 +14,7 @@ import zhCN from 'antd/locale/zh_CN'; ...@@ -14,7 +14,7 @@ import zhCN from 'antd/locale/zh_CN';
// // 汉化 // // 汉化
// dayjs.locale('zh-cn'); // dayjs.locale('zh-cn');
import App from './App.jsx'; import WebApp from './App.jsx';
import { GlobalProvider } from './layout/GlobalContext.jsx'; import { GlobalProvider } from './layout/GlobalContext.jsx';
...@@ -32,11 +32,13 @@ ReactDOM.createRoot(document.getElementById('root')).render( ...@@ -32,11 +32,13 @@ ReactDOM.createRoot(document.getElementById('root')).render(
}, },
}} }}
> >
<Router> <App>
<GlobalProvider> <Router>
<App /> <GlobalProvider>
</GlobalProvider> <WebApp />
</Router> </GlobalProvider>
</Router>
</App>
</ConfigProvider> </ConfigProvider>
</PersistGate> </PersistGate>
</Provider> </Provider>
......
import axios from '@/utils/axios';
// 优惠券列表
export function getList(data) {
return axios.post('/api/system/coupon/getList', data);
}
// 添加优惠券
export function addCoupon(data) {
return axios.post('/api/system/coupon/add', data);
}
// 修改优惠券
export function editCoupon(data) {
return axios.post('/api/system/coupon/edit', data);
}
// 删除优惠券
export function delCoupon(data) {
return axios.post('/api/system/coupon/del', data);
}
// 优惠券详情
export function getCoupon(data) {
return axios.post('/api/system/coupon/getInfoById', data);
}
// 优惠券发放
export function addCouponRecord(data) {
return axios.post('/api/system/coupon/record/add', data);
}
// 获取优惠券开关
export function getCouponConfig(data) {
return axios.post('/api/system/config/getCoupon', data);
}
// 设置优惠券开关
export function setCouponConfig(data) {
return axios.post('/api/system/config/couponEdit', data);
}
import { Drawer, Form, Input, DatePicker, Button, Space, message } from 'antd';
import { addCoupon, editCoupon, getCoupon } from '../api';
import { useEffect } from 'react';
import dayjs from 'dayjs';
const { RangePicker } = DatePicker;
export default function FormDrawer({ id, onComplete, ...rest }) {
const [form] = Form.useForm();
async function fetchInfo() {
if (!id) return;
const { data } = await getCoupon({ id });
const time = [dayjs(data.start_time), dayjs(data.end_time)];
form.setFieldsValue({ ...data, time });
}
useEffect(() => {
fetchInfo();
}, [id]);
const title = id ? '编辑优惠券' : '添加优惠券';
const onFinish = async (values) => {
const { time, ...params } = values;
if (time) {
params.start_time = new Date(time[0]).getTime() / 1000;
params.end_time = new Date(time[1]).getTime() / 1000;
}
const res = id ? await editCoupon({ ...params, id }) : await addCoupon(params);
message.success(res.message);
rest.onClose && rest.onClose();
onComplete && onComplete();
};
const onClose = () => {
form.resetFields();
rest.onClose && rest.onClose();
};
return (
<Drawer placement='right' title={title} {...rest} onClose={onClose}>
<Form form={form} onFinish={onFinish} labelCol={{ span: 6 }} autoComplete='off'>
<Form.Item
name='name'
label='优惠券名称'
rules={[
{ required: true, message: '请输入优惠券名称' },
{ max: 30, message: '最大30字符' },
]}
>
<Input placeholder='请输入优惠券名称' />
</Form.Item>
<Form.Item
name='norm_price'
label='达标金额'
rules={[
{ required: true, message: '请输入达标金额' },
{
validator: (_, value) => {
return new Promise((resolve, reject) => {
if (!value || /^[1-9]\d*$/.test(value)) {
resolve();
} else {
reject(new Error('必须是正整数'));
}
});
},
},
]}
>
<Input placeholder='请输入达标金额' />
</Form.Item>
<Form.Item
name='reduced_price'
label='满减金额'
rules={[
{ required: true, message: '请输入满减金额' },
{
validator: (_, value) => {
return new Promise((resolve, reject) => {
if (!value || /^[1-9]\d*$/.test(value)) {
resolve();
} else if (!(Number(value) <= Number(form.getFieldValue('norm_price')))) {
reject('小于等于达标金额');
}
});
},
},
]}
>
<Input placeholder='请输入满减金额' />
</Form.Item>
<Form.Item name='time' label='有效期' rules={[{ required: true, message: '请选择有效期' }]}>
<RangePicker></RangePicker>
</Form.Item>
<Form.Item style={{ textAlign: 'center' }}>
<Space size={20}>
<Button type='default' onClick={onClose}>
取消
</Button>
<Button type='primary' htmlType='submit'>
提交
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
);
}
import { Drawer, Form, Input, Select, Button, Space, App } from 'antd';
const { TextArea } = Input;
import { addCouponRecord } from '../api';
import { useEffect } from 'react';
export default function SendCouponDrawer({ couponList = [], ...rest }) {
const { message } = App.useApp();
const [form] = Form.useForm();
const onFinish = async (values) => {
const res = await addCouponRecord(values);
if (res.data.err.msg) {
message.error(res.data.err.title);
} else {
message.success(res.message);
rest.onClose && rest.onClose();
}
};
const onClose = () => {
form.resetFields();
rest.onClose && rest.onClose();
};
return (
<Drawer title='优惠券发放' placement='right' {...rest} onClose={onClose}>
<Form form={form} onFinish={onFinish} labelCol={{ span: 6 }} autoComplete='off'>
<Form.Item
name='phone'
label='用户手机号'
rules={[{ required: true, message: '请输入用户手机号' }]}
>
<TextArea
placeholder='请输入用户手机号,用英文逗号隔开,每次最多100个'
autoSize={{ minRows: 5, maxRows: 8 }}
></TextArea>
</Form.Item>
<Form.Item
name='id'
label='发放优惠券'
rules={[{ required: true, message: '请选择优惠券' }]}
>
<Select placeholder='请选择优惠券'>
{couponList.map((item) => (
<Select.Option value={item.id} key={item.id}>
{item.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item wrapperCol={{ offset: 5, span: 16 }} style={{ textAlign: 'center' }}>
<Space size={20}>
<Button className='cancel' onClick={onClose}>
取消
</Button>
<Button className='submit' htmlType='submit'>
提交
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
);
}
import React, { useEffect, useState, useRef } from 'react'; import { useEffect, useState, useRef } from 'react';
import { import { Input, Button, Space, Popover, Switch, Image, App } from 'antd';
Table, import { getCoupon, setCoupon } from './request';
Input,
Button,
Row,
Col,
Space,
DatePicker,
Select,
Drawer,
Popover,
Form,
Modal,
Switch,
Image,
} from 'antd';
const { RangePicker } = DatePicker;
import PaginationCom from '@/common/Pagination';
const { TextArea } = Input;
import {
getList,
addCoupon,
recordCoupon,
editCoupon,
delCoupon,
getCoupon,
setCoupon,
getInfoById,
} from './request';
import dayjs from 'dayjs';
import Add from '@/assets/images/icon/add.png'; import Add from '@/assets/images/icon/add.png';
import reload from '@/assets/images/icon/reload.png';
import reset from '@/assets/images/icon/reset.png';
import filter from '@/assets/images/icon/filter.png';
import TableCom from '@/common/TableCom/index';
import coupon from '@/assets/images/icon/coupon.png'; import coupon from '@/assets/images/icon/coupon.png';
import coupon2 from '@/assets/images/icon/coupon2.png'; import coupon2 from '@/assets/images/icon/coupon2.png';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import AppList from '@/components/AppList';
import FormDrawer from './components/FormDrawer';
import SendCouponDrawer from './components/SendCouponDrawer';
import * as api from './api';
const Coupon = () => { const Coupon = () => {
const [data, setData] = useState([]); const { modal } = App.useApp();
const [page_size, setpage_size] = useState(10);
const [page, setPage] = useState(1); const appListRef = useRef(null);
const [total, setTotal] = useState(4); const reload = async () => {
const [couponTel, setcouponTel] = useState(''); await appListRef.current.handleReload(true);
const [loading, setLoading] = useState(true); };
// 添加/编辑的弹框 const [drawerOpen, setDrawerOpen] = useState(false);
const [isEdit, setIsEdit] = useState(false);
const [delMoadal, setDelModal] = useState(false);
const [form] = Form.useForm();
const [form2] = Form.useForm();
// 获取操作权限 // 获取操作权限
const { operationPermissionsList } = useSelector((state) => state.user); const { operationPermissionsList } = useSelector((state) => state.user);
// 是否添加
const [isadd, setisadd] = useState(true);
// 操作某一项的id // 操作某一项的id
const [id, setid] = useState(0); const [id, setId] = useState('');
const [couponModal, setCouponModal] = useState(false);
const [initialValues, setinitialValues] = useState({
name: '',
norm_price: '',
reduced_price: '',
end_time: '',
});
const [filterObj, setfilterObj] = useState({
name: '',
});
const [switchFlag, setSwitchFlag] = useState(false); const [switchFlag, setSwitchFlag] = useState(false);
const [dateVal, setDateVal] = useState(''); const [couponList, setCouponList] = useState([]);
// 过滤 const [sendCouponDrawerOpen, setSendCouponDrawerOpen] = useState(false);
const handleFilter = (flag, val) => { // 添加
if (flag == 'startandend') { const handleAdd = () => {
let val2 = val || []; setId('');
filterObj['start_time'] = parseInt( setDrawerOpen(true);
new Date(dayjs(val2[0]).format('YYYY-MM-DD') + ' 00:00:00').getTime() / 1000,
);
const endDate = new Date(dayjs(val2[1])).getDate();
const endTime = parseInt(
new Date(dayjs(val2[1]).format('YYYY-MM-') + endDate + ' 23:59:59').getTime() / 1000,
);
filterObj['end_time'] = endTime;
setDateVal(val);
if (!val2.length) {
delete filterObj['start_time'];
delete filterObj['end_time'];
}
} else {
filterObj[flag] = val;
}
for (const key in filterObj) {
if (!filterObj[key]) {
delete filterObj[key];
}
}
setfilterObj({ ...filterObj });
if (!val) init(filterObj);
}; };
// 编辑
const handleEdit = async (id) => { const handleEdit = async (id) => {
setid(id); setId(id);
setisadd(false); setDrawerOpen(true);
const data = await getInfoById({ id });
let { end_time, start_time } = data;
form.setFieldsValue({ ...data, validity: [dayjs(start_time), dayjs(end_time)] });
setisadd(false);
setIsEdit(true);
}; };
const onClose = () => { const onClose = () => {
setIsEdit(false); setDrawerOpen(false);
};
// 添加/编辑优惠券
const submitForm = async (obj) => {
let { validity } = obj;
let start_time =
new Date(dayjs(validity[0]).format('YYYY-MM-DD') + ' 00:00:00').getTime() / 1000;
let end_time = new Date(dayjs(validity[1]).format('YYYY-MM-DD') + ' 23:59:59').getTime() / 1000;
let copyObj = { ...obj, end_time, start_time }; // 将变量初始化移到使用之前
delete copyObj.validity;
console.log('点击了提交表单');
console.log('isadd:', isadd);
console.log('copyObj:', copyObj);
let bool;
if (isadd) {
bool = await addCoupon(copyObj);
} else {
bool = await editCoupon({
...copyObj,
id,
});
}
if (!bool) return;
init(filterObj);
setIsEdit(false);
};
const handleReset = () => {
setfilterObj({ name: '' });
init();
}; };
// 发放优惠券 // 删除
const handleRecord = async (obj) => { const handleDelete = async (id) => {
const res = await recordCoupon(obj); modal.confirm({
title: '提示',
setCouponModal(false); content: '优惠券将永久删除,是否继续?',
onOk: async () => {
await api.delCoupon({ id });
reload();
},
});
}; };
const delSuccess = async () => {
const bool = await delCoupon({ id });
if (!bool) return;
setDelModal(false);
init();
};
const init = async (obj = {}) => {
setLoading(true);
const { total, list } = await getList({ page, page_size, ...obj });
setTotal(total);
setData(list);
setLoading(false);
};
// 获取优惠券开关状态 // 获取优惠券开关状态
const handleCoupon = async () => { const handleCoupon = async () => {
// if(!flag.current){
const { coupon_switch } = await getCoupon(); const { coupon_switch } = await getCoupon();
coupon_switch == '1' ? setSwitchFlag(true) : setSwitchFlag(false); coupon_switch == '1' ? setSwitchFlag(true) : setSwitchFlag(false);
// }else{
// flag.current=false
// }
}; };
const [swithcLoad, setSwitchLoad] = useState(false); const [swithcLoad, setSwitchLoad] = useState(false);
// 改变优惠券开关 // 改变优惠券开关
...@@ -179,19 +68,25 @@ const Coupon = () => { ...@@ -179,19 +68,25 @@ const Coupon = () => {
}, 500); }, 500);
handleCoupon(); handleCoupon();
}; };
useEffect(() => {
if (!isEdit) {
form.setFieldsValue(initialValues);
form.resetFields();
}
if (!couponModal) {
form2.setFieldsValue({ phone: '', id: null });
form2.resetFields();
}
}, [isEdit, couponModal]);
useEffect(() => { useEffect(() => {
handleCoupon(); handleCoupon();
}, []); }, []);
const remote = {
request: api.getList,
afterRequest(data) {
setCouponList(data.list);
return data;
},
};
const filters = [
{
label: '优惠券名称',
name: 'name',
element: <Input placeholder='请输入优惠券名称' allowClear />,
},
];
const columns = [ const columns = [
{ {
title: 'ID', title: 'ID',
...@@ -270,316 +165,80 @@ const Coupon = () => { ...@@ -270,316 +165,80 @@ const Coupon = () => {
<Button onClick={() => handleEdit(id)}>编辑</Button> <Button onClick={() => handleEdit(id)}>编辑</Button>
)} )}
{operationPermissionsList.includes('/setting/coupon/del') && ( {operationPermissionsList.includes('/setting/coupon/del') && (
<Button <Button onClick={() => handleDelete(id)}>删除</Button>
onClick={() => {
setid(id);
setDelModal(true);
}}
>
删除
</Button>
)} )}
</Space> </Space>
); );
}, },
}, },
]; ];
return (
<div className='classify'>
<Row justify={'space-between'}>
<Col className='form-devices-inline'>
<Form layout='inline'>
<Form.Item label='优惠券名称'>
<Input
autoComplete='off'
allowClear
placeholder='请输入优惠券名称'
value={filterObj.name}
onChange={(ev) => handleFilter('name', ev.target.value)}
id='name'
></Input>
</Form.Item>
<Space>
<Button
type='primary'
ghost
icon={
<span
style={{
display: 'inline-block',
width: '11px',
height: '10px',
pointerEvents: 'none',
}}
>
<Image src={reset} />
</span>
}
onClick={handleReset}
>
重置
</Button>
<Button
type='primary'
ghost
icon={
<span
style={{
display: 'inline-block',
width: '13px',
height: '12px',
pointerEvents: 'none',
}}
>
<Image src={filter} />
</span>
}
onClick={() => {
setPage(1);
Object.values(filterObj).some((item) => item) ? init(filterObj) : init();
}}
>
筛选
</Button>
<Button
type='primary'
ghost
icon={
<span
style={{
display: 'inline-block',
width: '10px',
height: '12px',
pointerEvents: 'none',
}}
>
<Image src={reload} />
</span>
}
onClick={() =>
Object.values(filterObj).some((item) => item) ? init(filterObj) : init()
}
>
刷新
</Button>
</Space>
</Form>
</Col>
<Col>
<Space>
<label htmlFor='coupon'>优惠券开关</label>
<Switch
checked={switchFlag}
loading={swithcLoad}
onChange={changeCoupon}
disabled={!operationPermissionsList.includes('/setting/coupon/getCoupon')}
></Switch>
{operationPermissionsList.includes('/setting/coupon/add') && (
<Button
type='primary'
disabled={!switchFlag}
ghost
icon={
<img
style={{
background: !switchFlag,
display: 'inline-block',
width: '13px',
height: '11px',
pointerEvents: 'none',
}}
src={!switchFlag ? coupon2 : coupon}
/>
}
onClick={() => setCouponModal(true)}
>
优惠券发放
</Button>
)}
{operationPermissionsList.includes('/setting/coupon/give') && (
<Button
type='primary'
ghost
icon={
<span
style={{
display: 'inline-block',
width: '13px',
height: '12px',
pointerEvents: 'none',
}}
>
<Image src={Add} />
</span>
}
onClick={() => {
setisadd(true);
setIsEdit(true);
}}
>
添加
</Button>
)}
</Space>
</Col>
</Row>
<br />
{TableCom({ columns, loading, data })}
<Modal
mask={false}
centered
open={delMoadal}
onOk={delSuccess}
onCancel={() => setDelModal(false)}
footer={(_, { OkBtn, CancelBtn }) => {
return (
<Row justify={'center'} className='delModal' style={{ marginBottom: '30px' }}>
<Space size={20}>
<CancelBtn className='cancel' />
<OkBtn className='true' />
</Space>
</Row>
);
}}
okText='确认'
>
<p style={{ textAlign: 'center', padding: '30px 0 18px', fontSize: 16 }}>
优惠券将永久删除,是否继续?
</p>
</Modal>
<br />
<PaginationCom
page_size={page_size}
setpage_size={setpage_size}
page={page}
init={init}
filterObj={filterObj}
setPage={setPage}
total={total}
/>
<Drawer placement='right' onClose={onClose} open={isEdit} mask={false}>
<Form form={form} onFinish={submitForm} labelCol={{ span: 6 }}>
<Form.Item
name='name'
label='优惠券名称'
rules={[
{ required: true, message: '请输入优惠券名称' },
{ max: 30, message: '最大30字符' },
]}
>
<Input autoComplete='off' placeholder='请输入优惠券名称'></Input>
</Form.Item>
<Form.Item
name='norm_price'
label='达标金额'
rules={[
{ required: true, message: '请输入达标金额' },
{
validator: (_, value) => {
return new Promise((resolve, reject) => {
if (!value || /^[1-9]\d*$/.test(value)) {
resolve();
} else {
reject(new Error('必须是正整数'));
}
});
},
},
]}
>
<Input autoComplete='off' placeholder='请输入达标金额'></Input>
</Form.Item>
<Form.Item const filterAside = (
name='reduced_price' <Space>
label='满减金额' <label htmlFor='coupon'>优惠券开关</label>
rules={[ <Switch
{ required: true, message: '请输入满减金额' }, checked={switchFlag}
{ loading={swithcLoad}
validator: (_, value) => { onChange={changeCoupon}
return new Promise((resolve, reject) => { disabled={!operationPermissionsList.includes('/setting/coupon/getCoupon')}
if (!value || /^[1-9]\d*$/.test(value)) { ></Switch>
resolve(); {operationPermissionsList.includes('/setting/coupon/add') && (
} else if (!(Number(value) <= Number(form.getFieldValue('norm_price')))) { <Button
reject('小于等于达标金额'); type='primary'
} disabled={!switchFlag}
}); ghost
}, icon={
}, <img
]} style={{
> background: !switchFlag,
<Input autoComplete='off' placeholder='请输入满减金额'></Input> display: 'inline-block',
</Form.Item> width: '13px',
<Form.Item height: '11px',
name='validity' pointerEvents: 'none',
label='有效期'
rules={[{ required: true, message: '请选择有效期' }]}
>
<RangePicker
value={dateVal}
onChange={(ev) => handleFilter('startandend', ev)}
id='createTime'
></RangePicker>
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 16 }} style={{ textAlign: 'center' }}>
<Space size={20}>
<Button className='cancel' onClick={() => setIsEdit(false)}>
取消
</Button>
<Button className='submit' htmlType='submit'>
提交
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
<Drawer
placement='right'
onClose={() => setCouponModal(false)}
open={couponModal}
mask={false}
>
<Form form={form2} onFinish={handleRecord} labelCol={{ span: 6 }}>
<Form.Item
name='phone'
label='用户手机号'
rules={[{ required: true, message: '请输入用户手机号' }]}
>
<TextArea
placeholder='请输入用户手机号,用英文逗号隔开,每次最多100个'
autoSize={{
minRows: 5,
maxRows: 8,
}} }}
></TextArea> src={!switchFlag ? coupon2 : coupon}
</Form.Item> />
<Form.Item }
name='id' onClick={() => setSendCouponDrawerOpen(true)}
label='发放优惠券' >
rules={[{ required: true, message: '请选择优惠券' }]} 优惠券发放
> </Button>
<Select placeholder='请选择优惠券'> )}
{data && {operationPermissionsList.includes('/setting/coupon/give') && (
data.map((item) => ( <Button
<Select.Option value={item.id} key={item.id}> type='primary'
{item.name} ghost
</Select.Option> icon={
))} <span
</Select> style={{
</Form.Item> display: 'inline-block',
<Form.Item wrapperCol={{ offset: 5, span: 16 }} style={{ textAlign: 'center' }}> width: '13px',
<Space size={20}> height: '12px',
<Button className='cancel' onClick={() => setCouponModal(false)}> pointerEvents: 'none',
取消 }}
</Button> >
<Button className='submit' htmlType='submit'> <Image src={Add} />
提交 </span>
</Button> }
</Space> onClick={handleAdd}
</Form.Item> >
</Form> 添加
</Drawer> </Button>
)}
</Space>
);
return (
<div className='classify'>
<AppList {...{ remote, filters, columns, filterAside }} ref={appListRef}></AppList>
{/* 添加编辑 */}
<FormDrawer open={drawerOpen} id={id} onClose={onClose} onComplete={reload}></FormDrawer>
{/* 优惠券发送 */}
<SendCouponDrawer
open={sendCouponDrawerOpen}
couponList={couponList}
onClose={() => setSendCouponDrawerOpen(false)}
></SendCouponDrawer>
</div> </div>
); );
}; };
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论