From e0b68607068e4e310e4b5b9d95fe02d8949f91f3 Mon Sep 17 00:00:00 2001 From: archer <545436317@qq.com> Date: Thu, 20 Jul 2023 20:38:14 +0800 Subject: [PATCH] user ui --- .../Icon/icons/light/billRecord.svg | 1 + .../components/Icon/icons/light/inform.svg | 1 + .../components/Icon/icons/light/loginout.svg | 1 + .../components/Icon/icons/light/payRecord.svg | 1 + client/src/components/Icon/index.tsx | 6 +- client/src/hooks/useScreen.ts | 21 -- client/src/pages/kb/detail/index.tsx | 4 +- .../src/pages/number/components/BillTable.tsx | 15 +- client/src/pages/number/components/Info.tsx | 139 ++++++++++ .../pages/number/components/InformTable.tsx | 78 +++--- .../number/components/PayRecordTable.tsx | 69 ++--- .../number/components/PromotionTable.tsx | 67 ----- client/src/pages/number/index.tsx | 248 ++++++------------ 13 files changed, 307 insertions(+), 344 deletions(-) create mode 100644 client/src/components/Icon/icons/light/billRecord.svg create mode 100644 client/src/components/Icon/icons/light/inform.svg create mode 100644 client/src/components/Icon/icons/light/loginout.svg create mode 100644 client/src/components/Icon/icons/light/payRecord.svg delete mode 100644 client/src/hooks/useScreen.ts create mode 100644 client/src/pages/number/components/Info.tsx delete mode 100644 client/src/pages/number/components/PromotionTable.tsx diff --git a/client/src/components/Icon/icons/light/billRecord.svg b/client/src/components/Icon/icons/light/billRecord.svg new file mode 100644 index 000000000..7842cfcad --- /dev/null +++ b/client/src/components/Icon/icons/light/billRecord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/components/Icon/icons/light/inform.svg b/client/src/components/Icon/icons/light/inform.svg new file mode 100644 index 000000000..e16e00d69 --- /dev/null +++ b/client/src/components/Icon/icons/light/inform.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/components/Icon/icons/light/loginout.svg b/client/src/components/Icon/icons/light/loginout.svg new file mode 100644 index 000000000..9bf4c43e5 --- /dev/null +++ b/client/src/components/Icon/icons/light/loginout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/components/Icon/icons/light/payRecord.svg b/client/src/components/Icon/icons/light/payRecord.svg new file mode 100644 index 000000000..c7ea56e68 --- /dev/null +++ b/client/src/components/Icon/icons/light/payRecord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/components/Icon/index.tsx b/client/src/components/Icon/index.tsx index b79dab2e9..09fd041db 100644 --- a/client/src/components/Icon/index.tsx +++ b/client/src/components/Icon/index.tsx @@ -63,7 +63,11 @@ const map = { qaImport: require('./icons/file/qaImport.svg').default, uploadFile: require('./icons/file/uploadFile.svg').default, closeLight: require('./icons/light/close.svg').default, - customTitle: require('./icons/light/customTitle.svg').default + customTitle: require('./icons/light/customTitle.svg').default, + billRecordLight: require('./icons/light/billRecord.svg').default, + informLight: require('./icons/light/inform.svg').default, + payRecordLight: require('./icons/light/payRecord.svg').default, + loginoutLight: require('./icons/light/loginout.svg').default }; export type IconName = keyof typeof map; diff --git a/client/src/hooks/useScreen.ts b/client/src/hooks/useScreen.ts deleted file mode 100644 index e7087cfd3..000000000 --- a/client/src/hooks/useScreen.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useMemo } from 'react'; -import { useMediaQuery } from '@chakra-ui/react'; - -interface Props { - defaultIsPc?: boolean; -} - -export function useScreen(data?: Props) { - const { defaultIsPc = false } = data || {}; - const [isPc] = useMediaQuery('(min-width: 900px)', { - ssr: false, - fallback: defaultIsPc - }); - - return { - isPc, - mediaLgMd: useMemo(() => (isPc ? 'lg' : 'md'), [isPc]), - mediaMdSm: useMemo(() => (isPc ? 'md' : 'sm'), [isPc]), - media: (pc: any, phone: any) => (isPc ? pc : phone) - }; -} diff --git a/client/src/pages/kb/detail/index.tsx b/client/src/pages/kb/detail/index.tsx index 8d83f8ae6..a7ac435ea 100644 --- a/client/src/pages/kb/detail/index.tsx +++ b/client/src/pages/kb/detail/index.tsx @@ -6,8 +6,8 @@ import { useForm } from 'react-hook-form'; import { useQuery } from '@tanstack/react-query'; import { useUserStore } from '@/store/user'; import { KbItemType } from '@/types/plugin'; -import { useScreen } from '@/hooks/useScreen'; import { getErrText } from '@/utils/tools'; +import { useGlobalStore } from '@/store/global'; import { type ComponentRef } from './components/Info'; import Tabs from '@/components/Tabs'; import dynamic from 'next/dynamic'; @@ -37,7 +37,7 @@ const Detail = ({ kbId, currentTab }: { kbId: string; currentTab: `${TabEnum}` } const theme = useTheme(); const { toast } = useToast(); const router = useRouter(); - const { isPc } = useScreen(); + const { isPc } = useGlobalStore(); const { kbDetail, getKbDetail } = useUserStore(); const tabList = useRef([ diff --git a/client/src/pages/number/components/BillTable.tsx b/client/src/pages/number/components/BillTable.tsx index 771fb883a..68765876f 100644 --- a/client/src/pages/number/components/BillTable.tsx +++ b/client/src/pages/number/components/BillTable.tsx @@ -21,6 +21,7 @@ import MyIcon from '@/components/Icon'; import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker'; import { addDays } from 'date-fns'; import dynamic from 'next/dynamic'; +import { useGlobalStore } from '@/store/global'; const BillDetail = dynamic(() => import('./BillDetail')); @@ -30,6 +31,7 @@ const BillTable = () => { from: addDays(new Date(), -7), to: new Date() }); + const { isPc } = useGlobalStore(); const { data: bills, @@ -38,6 +40,7 @@ const BillTable = () => { getData } = usePagination({ api: getUserBills, + pageSize: isPc ? 20 : 10, params: { dateStart: new Date(dateRange.from || new Date()).setHours(0, 0, 0, 0), dateEnd: new Date(dateRange.to || new Date()).setHours(23, 59, 59, 999) @@ -47,8 +50,8 @@ const BillTable = () => { const [billDetail, setBillDetail] = useState(); return ( - <> - + + @@ -78,27 +81,27 @@ const BillTable = () => { {!isLoading && bills.length === 0 && ( - + 无使用记录~ )} - + getData(1)} /> - + {!!billDetail && setBillDetail(undefined)} />} - + ); }; diff --git a/client/src/pages/number/components/Info.tsx b/client/src/pages/number/components/Info.tsx new file mode 100644 index 000000000..49543d355 --- /dev/null +++ b/client/src/pages/number/components/Info.tsx @@ -0,0 +1,139 @@ +import React, { useCallback } from 'react'; +import { Box, Flex, Button, useDisclosure } from '@chakra-ui/react'; +import { useForm } from 'react-hook-form'; +import { UserUpdateParams } from '@/types/user'; +import { putUserInfo } from '@/api/user'; +import { useToast } from '@/hooks/useToast'; +import { useGlobalStore } from '@/store/global'; +import { useUserStore } from '@/store/user'; +import { UserType } from '@/types/user'; +import { useRouter } from 'next/router'; +import { useQuery } from '@tanstack/react-query'; +import dynamic from 'next/dynamic'; +import { useSelectFile } from '@/hooks/useSelectFile'; +import { compressImg } from '@/utils/file'; +import { getErrText } from '@/utils/tools'; +import { feConfigs } from '@/store/static'; + +import Loading from '@/components/Loading'; +import Avatar from '@/components/Avatar'; + +const PayModal = dynamic(() => import('./PayModal'), { + loading: () => , + ssr: false +}); + +const UserInfo = () => { + const router = useRouter(); + const { userInfo, updateUserInfo, initUserInfo } = useUserStore(); + const { setLoading } = useGlobalStore(); + const { reset } = useForm({ + defaultValues: userInfo as UserType + }); + const { toast } = useToast(); + const { + isOpen: isOpenPayModal, + onClose: onClosePayModal, + onOpen: onOpenPayModal + } = useDisclosure(); + const { File, onOpen: onOpenSelectFile } = useSelectFile({ + fileType: '.jpg,.png', + multiple: false + }); + + const onclickSave = useCallback( + async (data: UserUpdateParams) => { + setLoading(true); + try { + await putUserInfo({ + avatar: data.avatar + }); + updateUserInfo({ + avatar: data.avatar + }); + reset(data); + toast({ + title: '更新数据成功', + status: 'success' + }); + } catch (error) { + toast({ + title: getErrText(error), + status: 'error' + }); + } + setLoading(false); + }, + [reset, setLoading, toast, updateUserInfo] + ); + + const onSelectFile = useCallback( + async (e: File[]) => { + const file = e[0]; + if (!file) return; + try { + const src = await compressImg({ + file, + maxW: 100, + maxH: 100 + }); + + onclickSave({ + ...userInfo, + avatar: src + }); + } catch (err: any) { + toast({ + title: typeof err === 'string' ? err : '头像选择异常', + status: 'warning' + }); + } + }, + [onclickSave, toast, userInfo] + ); + + useQuery(['init'], initUserInfo, { + onSuccess(res) { + reset(res); + } + }); + return ( + + + 头像: + + + + + + 账号: + {userInfo?.username} + + {feConfigs?.show_userDetail && ( + + + 余额: + + {userInfo?.balance} 元 + + + + + )} + + {isOpenPayModal && } + + + ); +}; + +export default UserInfo; diff --git a/client/src/pages/number/components/InformTable.tsx b/client/src/pages/number/components/InformTable.tsx index 6ed8aac51..96b862a6c 100644 --- a/client/src/pages/number/components/InformTable.tsx +++ b/client/src/pages/number/components/InformTable.tsx @@ -1,13 +1,5 @@ import React from 'react'; -import { - Box, - Flex, - Accordion, - AccordionItem, - AccordionButton, - AccordionPanel, - AccordionIcon -} from '@chakra-ui/react'; +import { Box, Flex, useTheme } from '@chakra-ui/react'; import { getInforms, readInform } from '@/api/user'; import { usePagination } from '@/hooks/usePagination'; import { useLoading } from '@/hooks/useLoading'; @@ -16,8 +8,8 @@ import { formatTimeToChatTime } from '@/utils/tools'; import MyIcon from '@/components/Icon'; const BillTable = () => { + const theme = useTheme(); const { Loading } = useLoading(); - const { data: informs, isLoading, @@ -31,11 +23,17 @@ const BillTable = () => { }); return ( - - + + {informs.map((item) => ( - { if (!item.read) { await readInform(item._id); @@ -43,35 +41,31 @@ const BillTable = () => { } }} > - - - - {!item.read && ( - - )} - {item.title} - - - {formatTimeToChatTime(item.time)} - - - - - - {item.content} - + + {item.title} + + {formatTimeToChatTime(item.time)} + + + + {item.content} + + {!item.read && ( + + )} + ))} - + {!isLoading && informs.length === 0 && ( - + 暂无通知~ @@ -79,12 +73,12 @@ const BillTable = () => { )} {total > pageSize && ( - + )} - + ); }; diff --git a/client/src/pages/number/components/PayRecordTable.tsx b/client/src/pages/number/components/PayRecordTable.tsx index fd1bb021d..e17f0d59b 100644 --- a/client/src/pages/number/components/PayRecordTable.tsx +++ b/client/src/pages/number/components/PayRecordTable.tsx @@ -59,45 +59,46 @@ const PayRecordTable = () => { return ( <> - -
- - - - - - - - - - - {payOrders.map((item) => ( - - - - - - - - ))} - -
订单号时间金额状态
{item.orderId} - {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'} - {formatPrice(item.price)}元{item.status} - {item.status === 'NOTPAY' && ( - - )} -
-
- {!isInitialLoading && payOrders.length === 0 && ( - + {!isInitialLoading && payOrders.length === 0 ? ( + 无支付记录~ + ) : ( + + + + + + + + + + + + + {payOrders.map((item) => ( + + + + + + + + ))} + +
订单号时间金额状态
{item.orderId} + {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'} + {formatPrice(item.price)}元{item.status} + {item.status === 'NOTPAY' && ( + + )} +
+
)} diff --git a/client/src/pages/number/components/PromotionTable.tsx b/client/src/pages/number/components/PromotionTable.tsx deleted file mode 100644 index 8b0dbbeb1..000000000 --- a/client/src/pages/number/components/PromotionTable.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react'; -import { Flex, Table, Thead, Tbody, Tr, Th, Td, TableContainer, Box } from '@chakra-ui/react'; -import { useLoading } from '@/hooks/useLoading'; -import dayjs from 'dayjs'; -import { getPromotionRecords } from '@/api/user'; -import { usePagination } from '@/hooks/usePagination'; -import { PromotionRecordType } from '@/api/response/user'; -import { PromotionTypeMap } from '@/constants/user'; -import MyIcon from '@/components/Icon'; - -const OpenApi = () => { - const { Loading } = useLoading(); - - const { - data: promotionRecords, - isLoading, - total, - pageSize, - Pagination - } = usePagination({ - api: getPromotionRecords - }); - - return ( - <> - - - - - - - - - - - {promotionRecords.map((item) => ( - - - - - - ))} - -
时间类型金额
- {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'} - {PromotionTypeMap[item.type]}{item.amount}
-
- - {!isLoading && promotionRecords.length === 0 && ( - - - - 无佣金记录~ - - - )} - {total > pageSize && ( - - - - )} - - - ); -}; - -export default OpenApi; diff --git a/client/src/pages/number/index.tsx b/client/src/pages/number/index.tsx index 075e9696d..e813ead85 100644 --- a/client/src/pages/number/index.tsx +++ b/client/src/pages/number/index.tsx @@ -1,212 +1,118 @@ import React, { useCallback, useRef } from 'react'; -import { Card, Box, Flex, Button, Grid, useDisclosure } from '@chakra-ui/react'; -import { useForm } from 'react-hook-form'; -import { UserUpdateParams } from '@/types/user'; -import { putUserInfo } from '@/api/user'; -import { useToast } from '@/hooks/useToast'; +import { Box, Flex, useTheme } from '@chakra-ui/react'; import { useGlobalStore } from '@/store/global'; -import { useUserStore } from '@/store/user'; -import { UserType } from '@/types/user'; -import { clearCookie } from '@/utils/user'; import { useRouter } from 'next/router'; -import { useQuery } from '@tanstack/react-query'; import dynamic from 'next/dynamic'; -import { useSelectFile } from '@/hooks/useSelectFile'; -import { compressImg } from '@/utils/file'; -import { getErrText } from '@/utils/tools'; -import { feConfigs } from '@/store/static'; +import { clearCookie } from '@/utils/user'; + +import PageContainer from '@/components/PageContainer'; +import SideTabs from '@/components/SideTabs'; -import Loading from '@/components/Loading'; -import Avatar from '@/components/Avatar'; -import MyIcon from '@/components/Icon'; import Tabs from '@/components/Tabs'; -import BillTable from './components/BillTable'; +import UserInfo from './components/Info'; +import { useUserStore } from '@/store/user'; +const BillTable = dynamic(() => import('./components/BillTable'), { + ssr: false +}); const PayRecordTable = dynamic(() => import('./components/PayRecordTable'), { ssr: false }); - const InformTable = dynamic(() => import('./components/InformTable'), { ssr: false }); -const PayModal = dynamic(() => import('./components/PayModal'), { - loading: () => , - ssr: false -}); -const WxConcat = dynamic(() => import('@/components/WxConcat'), { - loading: () => , - ssr: false -}); -enum TableEnum { +enum TabEnum { + 'info' = 'info', 'bill' = 'bill', 'pay' = 'pay', - 'promotion' = 'promotion', - 'inform' = 'inform' + 'inform' = 'inform', + 'loginout' = 'loginout' } -const NumberSetting = ({ tableType }: { tableType: `${TableEnum}` }) => { - const tableList = useRef([ - { label: '账单', id: TableEnum.bill, Component: }, - { label: '充值', id: TableEnum.pay, Component: }, - { label: '通知', id: TableEnum.inform, Component: } +const NumberSetting = ({ currentTab }: { currentTab: `${TabEnum}` }) => { + const tabList = useRef([ + { icon: 'meLight', label: '个人信息', id: TabEnum.info, Component: }, + { icon: 'billRecordLight', label: '消费记录', id: TabEnum.bill, Component: }, + { icon: 'payRecordLight', label: '充值记录', id: TabEnum.pay, Component: }, + { icon: 'informLight', label: '通知', id: TabEnum.inform, Component: }, + { icon: 'loginoutLight', label: '登出', id: TabEnum.loginout, Component: () => <> } ]); const router = useRouter(); - const { userInfo, updateUserInfo, initUserInfo, setUserInfo } = useUserStore(); - const { setLoading } = useGlobalStore(); - const { reset } = useForm({ - defaultValues: userInfo as UserType - }); - const { toast } = useToast(); - const { - isOpen: isOpenPayModal, - onClose: onClosePayModal, - onOpen: onOpenPayModal - } = useDisclosure(); + const theme = useTheme(); + const { isPc } = useGlobalStore(); + const { setUserInfo } = useUserStore(); - const { File, onOpen: onOpenSelectFile } = useSelectFile({ - fileType: '.jpg,.png', - multiple: false - }); - - const onclickSave = useCallback( - async (data: UserUpdateParams) => { - setLoading(true); - try { - await putUserInfo({ - avatar: data.avatar - }); - updateUserInfo({ - avatar: data.avatar - }); - reset(data); - toast({ - title: '更新数据成功', - status: 'success' - }); - } catch (error) { - toast({ - title: getErrText(error), - status: 'error' - }); - } - setLoading(false); - }, - [reset, setLoading, toast, updateUserInfo] - ); - - const onSelectFile = useCallback( - async (e: File[]) => { - const file = e[0]; - if (!file) return; - try { - const src = await compressImg({ - file, - maxW: 100, - maxH: 100 - }); - - onclickSave({ - ...userInfo, - avatar: src - }); - } catch (err: any) { - toast({ - title: typeof err === 'string' ? err : '头像选择异常', - status: 'warning' + const setCurrentTab = useCallback( + (tab: string) => { + if (tab === TabEnum.loginout) { + clearCookie(); + setUserInfo(null); + router.replace('/login'); + } else { + router.replace({ + query: { + currentTab: tab + } }); } }, - [onclickSave, toast, userInfo] + [router, setUserInfo] ); - const onclickLogOut = useCallback(() => { - clearCookie(); - setUserInfo(null); - router.replace('/login'); - }, [router, setUserInfo]); - - useQuery(['init'], initUserInfo, { - onSuccess(res) { - reset(res); - } - }); - return ( - - - - - - - 账号信息 - - - - - 头像: - - - - 账号: - {userInfo?.username} - - {feConfigs?.show_userDetail && ( - - - 余额: - - {userInfo?.balance} 元 - - - - - )} - - - - {feConfigs?.show_userDetail && ( - + + + {isPc ? ( + + + + ) : ( + router.replace(`/number?type=${id}`)} + w={'90%'} + size={isPc ? 'md' : 'sm'} + list={tabList.current.map((item) => ({ + id: item.id, + label: item.label + }))} + activeId={currentTab} + onChange={setCurrentTab} /> - - {(() => { - const item = tableList.current.find((item) => item.id === tableType); - - return item ? item.Component : null; - })()} - - + )} - - {isOpenPayModal && } - - + + + {currentTab === TabEnum.info && } + {currentTab === TabEnum.bill && } + {currentTab === TabEnum.pay && } + {currentTab === TabEnum.inform && } + +
+ ); }; export async function getServerSideProps({ query }: any) { return { props: { - tableType: query?.type || TableEnum.bill + currentTab: query?.currentTab || TabEnum.info } }; }