Commit 2463e11c by archer

feat: date picker

parent 4cbe4ebd
...@@ -13,17 +13,18 @@ ...@@ -13,17 +13,18 @@
"@alicloud/openapi-client": "^0.4.5", "@alicloud/openapi-client": "^0.4.5",
"@alicloud/tea-util": "^1.4.5", "@alicloud/tea-util": "^1.4.5",
"@chakra-ui/icons": "^2.0.17", "@chakra-ui/icons": "^2.0.17",
"@chakra-ui/react": "^2.5.1", "@chakra-ui/react": "^2.7.0",
"@chakra-ui/system": "^2.5.5", "@chakra-ui/system": "^2.5.8",
"@dqbd/tiktoken": "^1.0.7", "@dqbd/tiktoken": "^1.0.7",
"@emotion/react": "^11.10.6", "@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6", "@emotion/styled": "^11.10.6",
"@next/font": "13.1.6", "@next/font": "13.4.5",
"@tanstack/react-query": "^4.24.10", "@tanstack/react-query": "^4.24.10",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
"axios": "^1.3.3", "axios": "^1.3.3",
"cookie": "^0.5.0", "cookie": "^0.5.0",
"crypto": "^1.0.1", "crypto": "^1.0.1",
"date-fns": "^2.30.0",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"eventsource-parser": "^0.1.0", "eventsource-parser": "^0.1.0",
"formidable": "^2.1.1", "formidable": "^2.1.1",
...@@ -37,7 +38,7 @@ ...@@ -37,7 +38,7 @@
"mermaid": "^8.13.5", "mermaid": "^8.13.5",
"mongoose": "^6.10.0", "mongoose": "^6.10.0",
"nanoid": "^4.0.1", "nanoid": "^4.0.1",
"next": "13.1.6", "next": "13.4.5",
"nextjs-cors": "^2.1.2", "nextjs-cors": "^2.1.2",
"nodemailer": "^6.9.1", "nodemailer": "^6.9.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
...@@ -45,6 +46,7 @@ ...@@ -45,6 +46,7 @@
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"pg": "^8.10.0", "pg": "^8.10.0",
"react": "18.2.0", "react": "18.2.0",
"react-day-picker": "^8.7.1",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-hook-form": "^7.43.1", "react-hook-form": "^7.43.1",
"react-markdown": "^8.0.5", "react-markdown": "^8.0.5",
......
...@@ -66,7 +66,7 @@ export const loginOut = () => GET('/user/loginout'); ...@@ -66,7 +66,7 @@ export const loginOut = () => GET('/user/loginout');
export const putUserInfo = (data: UserUpdateParams) => PUT('/user/update', data); export const putUserInfo = (data: UserUpdateParams) => PUT('/user/update', data);
export const getUserBills = (data: RequestPaging) => export const getUserBills = (data: RequestPaging) =>
GET<PagingData<UserBillType>>(`/user/getBill?${Obj2Query(data)}`); POST<PagingData<UserBillType>>(`/user/getBill`, data);
export const getPayOrders = () => GET<PaySchema[]>(`/user/getPayOrders`); export const getPayOrders = () => GET<PaySchema[]>(`/user/getPayOrders`);
......
.datePicker {
--rdp-background-color: #d6e8ff;
--rdp-accent-color: #0000ff;
}
import React, { useState, useMemo, useRef } from 'react';
import { Box, Card, Flex, useTheme, useOutsideClick, Button } from '@chakra-ui/react';
import { addDays, format } from 'date-fns';
import { type DateRange, DayPicker } from 'react-day-picker';
import MyIcon from '../Icon';
import 'react-day-picker/dist/style.css';
import styles from './index.module.scss';
import zhCN from 'date-fns/locale/zh-CN';
const DateRangePicker = ({
onChange,
onSuccess,
position = 'bottom',
defaultDate = {
from: addDays(new Date(), -30),
to: new Date()
}
}: {
onChange?: (date: DateRange) => void;
onSuccess?: (date: DateRange) => void;
position?: 'bottom' | 'top';
defaultDate?: DateRange;
}) => {
const theme = useTheme();
const OutRangeRef = useRef(null);
const [range, setRange] = useState<DateRange | undefined>(defaultDate);
const [showSelected, setShowSelected] = useState(false);
const formatSelected = useMemo(() => {
if (range?.from && range.to) {
return `${format(range.from, 'y-MM-dd')} ~ ${format(range.to, 'y-MM-dd')}`;
}
return `${format(new Date(), 'y-MM-dd')} ~ ${format(new Date(), 'y-MM-dd')}`;
}, [range]);
useOutsideClick({
ref: OutRangeRef,
handler: () => {
setShowSelected(false);
}
});
return (
<Box position={'relative'} ref={OutRangeRef}>
<Flex
border={theme.borders.base}
px={3}
py={1}
borderRadius={'sm'}
cursor={'pointer'}
bg={'myWhite.600'}
fontSize={'sm'}
onClick={() => setShowSelected(true)}
>
<Box>{formatSelected}</Box>
<MyIcon ml={2} name={'date'} w={'16px'} color={'myGray.600'} />
</Flex>
{showSelected && (
<Card
position={'absolute'}
zIndex={1}
{...(position === 'top'
? {
bottom: '40px'
}
: {})}
>
<DayPicker
locale={zhCN}
id="test"
mode="range"
className={styles.datePicker}
defaultMonth={defaultDate.to}
selected={range}
disabled={[
{ from: new Date(2022, 3, 1), to: addDays(new Date(), -90) },
{ from: addDays(new Date(), 1), to: new Date(2099, 1, 1) }
]}
onSelect={(date) => {
if (date?.from === undefined) {
date = {
from: range?.from,
to: range?.from
};
}
if (date?.to === undefined) {
date.to = date.from;
}
setRange(date);
onChange && onChange(date);
}}
footer={
<Flex justifyContent={'flex-end'}>
<Button
variant={'outline'}
size={'sm'}
mr={2}
onClick={() => setShowSelected(false)}
>
取消
</Button>
<Button
size={'sm'}
onClick={() => {
onSuccess && onSuccess(range || defaultDate);
setShowSelected(false);
}}
>
确认
</Button>
</Flex>
}
/>
</Card>
)}
</Box>
);
};
export default DateRangePicker;
export type DateRangeType = DateRange;
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1686832863390" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4120" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M782.84 188.75h-43.15v-60.46c0-16.57-13.43-30-30-30s-30 13.43-30 30v60.46H371.88v-60.46c0-16.57-13.43-30-30-30s-30 13.43-30 30v60.46H250.5c-66.17 0-120 53.83-120 120v494.47c0 66.17 53.83 120 120 120h532.33c66.17 0 120-53.83 120-120V308.75c0.01-66.17-53.82-120-119.99-120z m-532.34 60h61.37v133.63c0 16.57 13.43 30 30 30s30-13.43 30-30V248.75h307.81v133.63c0 16.57 13.43 30 30 30s30-13.43 30-30V248.75h43.15c33.08 0 60 26.92 60 60V649.5H190.5V308.75c0-33.08 26.92-60 60-60z m532.34 614.47H250.5c-33.08 0-60-26.92-60-60V709.5h652.33v93.72c0.01 33.08-26.91 60-59.99 60z" p-id="4121"></path></svg>
\ No newline at end of file
...@@ -33,7 +33,8 @@ const map = { ...@@ -33,7 +33,8 @@ const map = {
export: require('./icons/export.svg').default, export: require('./icons/export.svg').default,
text: require('./icons/text.svg').default, text: require('./icons/text.svg').default,
history: require('./icons/history.svg').default, history: require('./icons/history.svg').default,
kbTest: require('./icons/kbTest.svg').default kbTest: require('./icons/kbTest.svg').default,
date: require('./icons/date.svg').default
}; };
export type IconName = keyof typeof map; export type IconName = keyof typeof map;
......
...@@ -39,7 +39,7 @@ const Button = defineStyleConfig({ ...@@ -39,7 +39,7 @@ const Button = defineStyleConfig({
}, },
sm: { sm: {
fontSize: 'sm', fontSize: 'sm',
px: 3, px: 4,
py: 0, py: 0,
fontWeight: 'normal', fontWeight: 'normal',
height: '26px', height: '26px',
......
...@@ -76,6 +76,20 @@ export const usePagination = <T = any,>({ ...@@ -76,6 +76,20 @@ export const usePagination = <T = any,>({
mutate(+e.target.value); mutate(+e.target.value);
} }
}} }}
onKeyDown={(e) => {
// @ts-ignore
const val = +e.target.value;
if (val && e.keyCode === 13) {
if (val === pageNum) return;
if (val >= maxPage) {
mutate(maxPage);
} else if (val < 1) {
mutate(1);
} else {
mutate(val);
}
}
}}
/> />
<Box mx={2}>/</Box> <Box mx={2}>/</Box>
{maxPage} {maxPage}
......
...@@ -4,23 +4,32 @@ import { jsonRes } from '@/service/response'; ...@@ -4,23 +4,32 @@ import { jsonRes } from '@/service/response';
import { connectToDatabase, Bill } from '@/service/mongo'; import { connectToDatabase, Bill } from '@/service/mongo';
import { authUser } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
import { adaptBill } from '@/utils/adapt'; import { adaptBill } from '@/utils/adapt';
import { addDays } from 'date-fns';
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
let { pageNum = 1, pageSize = 10 } = req.query as { const {
pageNum: string; pageNum = 1,
pageSize: string; pageSize = 10,
dateStart = addDays(new Date(), -7),
dateEnd = new Date()
} = req.body as {
pageNum: number;
pageSize: number;
dateStart: Date;
dateEnd: Date;
}; };
pageNum = +pageNum;
pageSize = +pageSize;
const { userId } = await authUser({ req, authToken: true }); const { userId } = await authUser({ req, authToken: true });
await connectToDatabase(); await connectToDatabase();
const where = { const where = {
userId userId,
time: {
$gte: new Date(dateStart).setHours(0, 0, 0, 0),
$lte: new Date(dateEnd).setHours(23, 59, 59, 999)
}
}; };
// get bill record and total by record // get bill record and total by record
......
import React from 'react'; import React, { useState } from 'react';
import { Table, Thead, Tbody, Tr, Th, Td, TableContainer, Flex, Box } from '@chakra-ui/react'; import { Table, Thead, Tbody, Tr, Th, Td, TableContainer, Flex, Box } from '@chakra-ui/react';
import { BillTypeMap } from '@/constants/user'; import { BillTypeMap } from '@/constants/user';
import { getUserBills } from '@/api/user'; import { getUserBills } from '@/api/user';
...@@ -7,18 +7,29 @@ import { usePagination } from '@/hooks/usePagination'; ...@@ -7,18 +7,29 @@ import { usePagination } from '@/hooks/usePagination';
import { useLoading } from '@/hooks/useLoading'; import { useLoading } from '@/hooks/useLoading';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import MyIcon from '@/components/Icon'; import MyIcon from '@/components/Icon';
import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker';
import { addDays } from 'date-fns';
const BillTable = () => { const BillTable = () => {
const { Loading } = useLoading(); const { Loading } = useLoading();
const [dateRange, setDateRange] = useState<DateRangeType>({
from: addDays(new Date(), -7),
to: new Date()
});
const { const {
data: bills, data: bills,
isLoading, isLoading,
Pagination, Pagination,
pageSize, pageSize,
total total,
getData
} = usePagination<UserBillType>({ } = usePagination<UserBillType>({
api: getUserBills api: getUserBills,
params: {
dateStart: dateRange.from,
dateEnd: dateRange.to
}
}); });
return ( return (
...@@ -48,8 +59,6 @@ const BillTable = () => { ...@@ -48,8 +59,6 @@ const BillTable = () => {
))} ))}
</Tbody> </Tbody>
</Table> </Table>
<Loading loading={isLoading} fixed={false} />
</TableContainer> </TableContainer>
{!isLoading && bills.length === 0 && ( {!isLoading && bills.length === 0 && (
...@@ -62,9 +71,18 @@ const BillTable = () => { ...@@ -62,9 +71,18 @@ const BillTable = () => {
)} )}
{total > pageSize && ( {total > pageSize && (
<Flex w={'100%'} mt={4} justifyContent={'flex-end'}> <Flex w={'100%'} mt={4} justifyContent={'flex-end'}>
<DateRangePicker
defaultDate={dateRange}
position="top"
onChange={setDateRange}
onSuccess={() => getData(1)}
/>
<Box ml={2}>
<Pagination /> <Pagination />
</Box>
</Flex> </Flex>
)} )}
<Loading loading={isLoading} fixed={false} />
</> </>
); );
}; };
......
...@@ -44,8 +44,6 @@ const OpenApi = () => { ...@@ -44,8 +44,6 @@ const OpenApi = () => {
))} ))}
</Tbody> </Tbody>
</Table> </Table>
<Loading loading={isLoading} fixed={false} />
</TableContainer> </TableContainer>
{!isLoading && promotionRecords.length === 0 && ( {!isLoading && promotionRecords.length === 0 && (
...@@ -61,6 +59,7 @@ const OpenApi = () => { ...@@ -61,6 +59,7 @@ const OpenApi = () => {
<Pagination /> <Pagination />
</Flex> </Flex>
)} )}
<Loading loading={isLoading} fixed={false} />
</> </>
); );
}; };
......
...@@ -19,30 +19,24 @@ import Loading from '@/components/Loading'; ...@@ -19,30 +19,24 @@ import Loading from '@/components/Loading';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import MyIcon from '@/components/Icon'; import MyIcon from '@/components/Icon';
import Tabs from '@/components/Tabs'; import Tabs from '@/components/Tabs';
import BillTable from './components/BillTable';
const PayRecordTable = dynamic(() => import('./components/PayRecordTable'), { const PayRecordTable = dynamic(() => import('./components/PayRecordTable'), {
loading: () => <Loading fixed={false} />, ssr: true
ssr: false
});
const BilTable = dynamic(() => import('./components/BillTable'), {
loading: () => <Loading fixed={false} />,
ssr: false
}); });
const PromotionTable = dynamic(() => import('./components/PromotionTable'), { const PromotionTable = dynamic(() => import('./components/PromotionTable'), {
loading: () => <Loading fixed={false} />, ssr: true
ssr: false
}); });
const InformTable = dynamic(() => import('./components/InformTable'), { const InformTable = dynamic(() => import('./components/InformTable'), {
loading: () => <Loading fixed={false} />, ssr: true
ssr: false
}); });
const PayModal = dynamic(() => import('./components/PayModal'), { const PayModal = dynamic(() => import('./components/PayModal'), {
loading: () => <Loading fixed={false} />, loading: () => <Loading fixed={false} />,
ssr: false ssr: true
}); });
const WxConcat = dynamic(() => import('@/components/WxConcat'), { const WxConcat = dynamic(() => import('@/components/WxConcat'), {
loading: () => <Loading fixed={false} />, loading: () => <Loading fixed={false} />,
ssr: false ssr: true
}); });
enum TableEnum { enum TableEnum {
...@@ -54,7 +48,7 @@ enum TableEnum { ...@@ -54,7 +48,7 @@ enum TableEnum {
const NumberSetting = ({ tableType }: { tableType: `${TableEnum}` }) => { const NumberSetting = ({ tableType }: { tableType: `${TableEnum}` }) => {
const tableList = useRef([ const tableList = useRef([
{ label: '账单', id: TableEnum.bill, Component: <BilTable /> }, { label: '账单', id: TableEnum.bill, Component: <BillTable /> },
{ label: '充值', id: TableEnum.pay, Component: <PayRecordTable /> }, { label: '充值', id: TableEnum.pay, Component: <PayRecordTable /> },
{ label: '佣金', id: TableEnum.promotion, Component: <PromotionTable /> }, { label: '佣金', id: TableEnum.promotion, Component: <PromotionTable /> },
{ label: '通知', id: TableEnum.inform, Component: <InformTable /> } { label: '通知', id: TableEnum.inform, Component: <InformTable /> }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment