From 50237f3f75787917fdddd35faaae5fb604275e1c Mon Sep 17 00:00:00 2001 From: guofei <1633295391@qq.com> Date: Mon, 10 Jun 2024 21:20:29 +0800 Subject: [PATCH] feat: success reset 60% --- package.json | 1 + pnpm-lock.yaml | 3 + src/layouts/UserDropdown.tsx | 54 ++++++++ src/layouts/index.tsx | 8 +- src/pages/User/Dilatation.tsx | 107 ++++++++++++++++ src/pages/User/UserDetails.tsx | 151 +++++++++++++++++++++-- src/pages/User/index.tsx | 72 ++++++++++- src/services/system/order/typing.d.ts | 31 +++++ src/services/system/supderAdmin/index.ts | 4 + src/services/system/user/index.ts | 8 ++ src/services/system/user/typing.d.ts | 2 + src/utils/const.ts | 16 +++ src/utils/format.ts | 7 ++ 13 files changed, 447 insertions(+), 17 deletions(-) create mode 100644 src/layouts/UserDropdown.tsx create mode 100644 src/pages/User/Dilatation.tsx create mode 100644 src/services/system/order/typing.d.ts create mode 100644 src/utils/const.ts diff --git a/package.json b/package.json index e42aa10..a915636 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "antd": "^5.18.0", "antd-style": "^3.6.2", "axios": "^1.7.2", + "dayjs": "^1.11.11", "lodash-es": "^4.17.21", "qs": "^6.12.1", "tailwindcss": "^3.4.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ee45da..e8257af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: axios: specifier: ^1.7.2 version: 1.7.2 + dayjs: + specifier: ^1.11.11 + version: 1.11.11 lodash-es: specifier: ^4.17.21 version: 4.17.21 diff --git a/src/layouts/UserDropdown.tsx b/src/layouts/UserDropdown.tsx new file mode 100644 index 0000000..6ddb54e --- /dev/null +++ b/src/layouts/UserDropdown.tsx @@ -0,0 +1,54 @@ +import { userLogoutAPI } from '@/services/system/supderAdmin'; +import { history } from '@umijs/max'; +import { Dropdown } from 'antd'; +import { JSXElementConstructor, ReactElement, ReactNode, ReactPortal } from 'react'; + +enum DrowType { + LOGOUT = 'LOGOUT', +} + +const handleLogout = () => { + userLogoutAPI().then(({ code }) => { + if (code === 200) { + history.replace('/login'); + } + }); +}; + +const UserDropDownRender = (props: { + dom: + | string + | number + | boolean + | ReactElement> + | Iterable + | ReactPortal + | null + | undefined; +}) => { + const handleType = { + [DrowType.LOGOUT]: handleLogout, + }; + + return ( + <> + { + handleType[key as DrowType](); + }, + }} + > + {props.dom} + + + ); +}; + +export { UserDropDownRender }; diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index a8e58aa..8bc6e52 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -1,12 +1,14 @@ import { PageContainer, ProLayout } from '@ant-design/pro-components'; -import { Link, Outlet, useAppData, useRouteProps } from '@umijs/max'; +import { Link, Outlet, useAppData, useModel, useRouteProps } from '@umijs/max'; import { App, ConfigProvider } from 'antd'; import zh_CN from 'antd/locale/zh_CN'; import React from 'react'; +import { UserDropDownRender } from './UserDropdown'; export const BasicLayout: React.FC = () => { const { clientRoutes } = useAppData(); const { useContainer } = useRouteProps(); + const { initialState } = useModel('@@initialState'); return ( @@ -16,9 +18,9 @@ export const BasicLayout: React.FC = () => { logo={null} layout="mix" avatarProps={{ - src: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png', size: 'small', - title: 'xxx', + title: initialState?.userName, + render: (props, dom) => , }} route={clientRoutes.find((e) => e.path === '/')} contentStyle={{ diff --git a/src/pages/User/Dilatation.tsx b/src/pages/User/Dilatation.tsx new file mode 100644 index 0000000..56815cc --- /dev/null +++ b/src/pages/User/Dilatation.tsx @@ -0,0 +1,107 @@ +import { userDilatationAPI } from '@/services/system/user'; +import { ActionType, ProFormDependency, ProFormRadio, ProFormText } from '@ant-design/pro-components'; +import { App, Form, Modal, Spin } from 'antd'; +import { useEffect, useState } from 'react'; + +type PropTypes = { + visible: boolean; + onCancel: () => void; + tableRef: React.RefObject>; + editRow: EditRow; +}; + +const DilatationModel = (props: PropTypes) => { + const { message } = App.useApp(); + const [form] = Form.useForm(); + const [loading, setLoading] = useState(false); + + const { visible, editRow, tableRef, onCancel } = props; + const [title, setTitle] = useState(); + + const handleOnCancel = () => { + form.resetFields(); + onCancel(); + }; + + const handleSubmit = async () => { + const values = await form.validateFields(); + if (!/^[0-9]+$/.test(values.size)) { + message.warning('请输入数字'); + return; + } + if (parseInt(values.size) <= 0) { + message.warning('扩容大小需大于0'); + return; + } + try { + setLoading(true); + const result = await userDilatationAPI({ userId: editRow!.id, type: values.type, addSize: values.size }); + if (result.code === 200) { + message.success(result.message); + handleOnCancel(); + tableRef.current?.reload(); + } + } finally { + setLoading(false); + } + }; + + useEffect(() => { + setTitle('用户扩容'); + }, []); + + return ( + + +
+ + + + {({ type, size }) => { + return ( +
+ 给{editRow?.email}账号,扩容{size} {type} +
+ ); + }} +
+ +
+
+ ); +}; + +export default DilatationModel; diff --git a/src/pages/User/UserDetails.tsx b/src/pages/User/UserDetails.tsx index e295a9a..5dfeb0b 100644 --- a/src/pages/User/UserDetails.tsx +++ b/src/pages/User/UserDetails.tsx @@ -1,6 +1,8 @@ import { getUserByIdAPI } from '@/services/system/user'; +import { UxOrderStatus, UxOrderStatusTag } from '@/utils/const'; +import { formatDateTime } from '@/utils/format'; import { ActionType } from '@ant-design/pro-components'; -import { Descriptions, Form, Modal, Spin, Tabs } from 'antd'; +import { Descriptions, Empty, Form, Modal, Spin, Table, Tabs } from 'antd'; import { useEffect, useState } from 'react'; type PropTypes = { @@ -17,6 +19,7 @@ const UserDetailsModal = (props: PropTypes) => { const [userInfo, setUserInfo] = useState(); const [projectCount, setProjectCount] = useState(0); const [materialsCount, setMaterialsCount] = useState(0); + const [dilatationLogs, setDilatationLogs] = useState([]); const [materialsGroup, setMaterialsGroup] = useState([]); const { visible, editRow, onCancel } = props; @@ -34,6 +37,7 @@ const UserDetailsModal = (props: PropTypes) => { if (result.code === 200) { setUserInfo(result.data.userInfo); setProjectCount(result.data.projectCount); + setDilatationLogs(result.data.dilatationLogs); setMaterialsCount(result.data.materialsCount); setMaterialsGroup(result.data.materialsGroup); } @@ -53,6 +57,21 @@ const UserDetailsModal = (props: PropTypes) => { } }, [visible]); + const RenderPackage = ({ userInfo }: { userInfo: User.UserItem }) => { + if (userInfo.package) { + return ( + userInfo?.package?.name + + '/' + + userInfo?.package?.showMoney + + '元' + + '/' + + userInfo?.package?.materialSpace + + userInfo?.package?.type + ); + } + return '-'; + }; + const RenderUserDetails = () => { if (!userInfo) return <>; return ( @@ -60,22 +79,16 @@ const UserDetailsModal = (props: PropTypes) => { {userInfo.email} {userInfo.userName} - {userInfo?.package?.name + - '/' + - userInfo?.package?.showMoney + - '元' + - '/' + - userInfo?.package?.materialSpace + - userInfo?.package?.type} + - {userInfo?.increasedStorageInKb && (userInfo.increasedStorageInKb / 1024).toFixed(2) + 'MB'} + {userInfo?.increasedStorageInKb && (userInfo.increasedStorageInKb / 1024).toFixed(2)}MB - {userInfo?.UserProfile && (userInfo.UserProfile.storageUsedInKb / 1024).toFixed(2) + 'MB'} + {userInfo?.UserProfile && ((userInfo?.UserProfile?.storageUsedInKb ?? 0) / 1024).toFixed(2)}MB - {((userInfo.increasedStorageInKb! - userInfo!.UserProfile!.storageUsedInKb) / 1024).toFixed(2) + 'MB'} + {((userInfo.increasedStorageInKb! - (userInfo?.UserProfile?.storageUsedInKb ?? 0)) / 1024).toFixed(2)}MB {projectCount} {materialsCount} @@ -94,6 +107,114 @@ const UserDetailsModal = (props: PropTypes) => { ); }; + const RenderUserOrders = () => { + if (userInfo && userInfo.Order!.length === 0) { + return ( + <> + + + ); + } + return ( + { + if (record.isApply === true) { + return record.orderNo + '(试用)'; + } + return record.orderNo; + }, + }, + { + title: '第三方订单号', + dataIndex: 'platformOrderStatus', + align: 'center', + }, + { + title: '支付类型', + dataIndex: 'payType', + align: 'center', + render: (_, record: Order.OrderItem) => { + // 支付类型 1. 微信 2. 支付宝 3. paypal -1: 后台创建 + if (record.payType === 1) { + return '微信'; + } else if (record.payType === 2) { + return '支付宝'; + } else if (record.payType === 3) { + return 'paypal'; + } + return '后台创建'; + }, + }, + { + title: '套餐', + dataIndex: 'goodsName', + align: 'center', + }, + { + title: '订单状态', + dataIndex: 'orderStatus', + align: 'center', + render: (_, record: Order.OrderItem) => { + const color = Reflect.get(UxOrderStatusTag, record.orderStatus); + return ( +
+
+ {Reflect.get(UxOrderStatus, record.orderStatus)} +
+ ); + }, + }, + { + title: '订单创建时间', + dataIndex: 'createdDateTime', + align: 'center', + }, + ]} + >
+ ); + }; + + const RenderDilatationLogs = () => { + if (dilatationLogs.length === 0) { + return ( + <> + + + ); + } + return ( + { + return record.size + record.type; + }, + }, + { + title: '扩容时间', + dataIndex: 'createDate', + align: 'center', + render: (_, record: User.UserItemByInfo['dilatationLogs'][0]) => formatDateTime(record.createDate), + }, + { + title: '备注', + dataIndex: 'desc', + align: 'center', + }, + ]} + >
+ ); + }; + return ( { - - + + + + + + diff --git a/src/pages/User/index.tsx b/src/pages/User/index.tsx index 28ec9fd..b351fc5 100644 --- a/src/pages/User/index.tsx +++ b/src/pages/User/index.tsx @@ -1,8 +1,11 @@ import { changeStatusAPI, delUxUserAPI, getUserListAPI } from '@/services/system/user'; import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components'; -import { App, Button, Space, TableProps, message } from 'antd'; +import { App, Button, Radio, Space, TableProps, message } from 'antd'; + +import { formatDateTime } from '@/utils/format'; import { useRef, useState } from 'react'; import CreateModel from './CreateModule'; +import DilatationModel from './Dilatation'; import OpenPackageModel from './OpenPackage'; import UserDetailsModal from './UserDetails'; @@ -13,6 +16,9 @@ const Page = () => { const [createModalVisible, setCreateModalVisible] = useState(false); const [openPackageVisible, setOpenPackageVisible] = useState(false); const [userDetailsVisible, setUserDetailsVisible] = useState(false); + const [kuoRongVisible, setKuoRongVisible] = useState(false); + const [showType, setShowType] = useState('MB'); + const [showTypeValue, setShowTypeValue] = useState(1024); const [editRow, setEditRow] = useState>(); const [tableParams, setTableParams] = useState({ pagination: { @@ -43,6 +49,11 @@ const Page = () => { }); }; + const handledDilatation = (record: User.UserItem) => { + setEditRow(record); + setKuoRongVisible(true); + }; + const columns: ProColumns[] = [ { title: '邮箱', @@ -89,6 +100,45 @@ const Page = () => { ); }, }, + { + title: '剩余容量', + dataIndex: 'packageId', + align: 'center', + hideInSearch: true, + renderText: (_, record: User.UserItem) => { + return ( +
+ {((record.increasedStorageInKb! - (record?.UserProfile?.storageUsedInKb ?? 0)) / showTypeValue).toFixed(2) + + showType} + +
+ ); + }, + filterDropdown: () => { + return ( +
+ 显示类型: + { + const type = e.target.value; + setShowType(type); + if (type === 'GB') { + setShowTypeValue(Math.pow(1024, 2)); + } else { + setShowTypeValue(1024); + } + }} + value={showType} + > + MB + GB + +
+ ); + }, + }, { title: '用户来源', dataIndex: 'userSource', @@ -126,17 +176,27 @@ const Page = () => { ); }, }, + { + title: '套餐过期时间', + dataIndex: 'expired', + align: 'center', + hideInSearch: true, + renderText: (_, record: User.UserItem) => (!record.expired ? '-' : formatDateTime(record.expired)), + }, { title: '账户创建时间', dataIndex: 'createdDateTime', align: 'center', hideInSearch: true, + renderText: (_, record: User.UserItem) => + !record.createdDateTime ? '-' : formatDateTime(record.createdDateTime), }, { title: '最后登录时间', dataIndex: 'lastLoginTime', align: 'center', hideInSearch: true, + renderText: (_, record: User.UserItem) => (!record.lastLoginTime ? '-' : formatDateTime(record.lastLoginTime)), }, { title: '操作', @@ -271,6 +331,16 @@ const Page = () => { tableRef={tableRef} editRow={editRow} /> + {/* 扩容 */} + { + setEditRow(undefined); + setKuoRongVisible(false); + }} + tableRef={tableRef} + editRow={editRow} + /> ); }; diff --git a/src/services/system/order/typing.d.ts b/src/services/system/order/typing.d.ts new file mode 100644 index 0000000..0dbe4d5 --- /dev/null +++ b/src/services/system/order/typing.d.ts @@ -0,0 +1,31 @@ +declare namespace Order { + type OrderItem = { + id?: string; + userEmail: string; + userId: string; + userName?: string; + user?: User.UserItem; + payType: number; + payTypeStr: string; + orderNo: string; + packageId: string; + package?: Package.PackageItem; + packageJson?: string; + orderStatus: number; + platformOrderStatus?: string; + platformNo?: string; + goodsName: string; + price: number; + quantity?: number; + payCode?: string; + payCodeExpired?: string; + platformNotifyTime?: string; + orderType: number; + createSource: number; + createSouceBy?: string; + createdDateTime: string; + lastUpdateDateTime?: string; + comment?: string; + isApply: boolean; + }; +} diff --git a/src/services/system/supderAdmin/index.ts b/src/services/system/supderAdmin/index.ts index 39aa849..6278bc9 100644 --- a/src/services/system/supderAdmin/index.ts +++ b/src/services/system/supderAdmin/index.ts @@ -19,3 +19,7 @@ export const editSupderAdminAPI = (data: SuperAdmin.SuperAdminItem): Promise => { return request.delete(`/system/superAdmin/${id}`); }; + +export const userLogoutAPI = (): Promise => { + return request.post(`/system/superAdmin/logout`); +}; diff --git a/src/services/system/user/index.ts b/src/services/system/user/index.ts index 524b067..641425a 100644 --- a/src/services/system/user/index.ts +++ b/src/services/system/user/index.ts @@ -24,3 +24,11 @@ export const delUxUserAPI = (id: string): Promise => { export const openPackageAPI = (params: User.UserItem): Promise => { return request.post('/system/user/openPackage', params); }; + +export const userDilatationAPI = (params: { + userId: string; + addSize: number; + type: string; +}): Promise => { + return request.post('/system/user/dilatation', params); +}; diff --git a/src/services/system/user/typing.d.ts b/src/services/system/user/typing.d.ts index 7f81225..4e50602 100644 --- a/src/services/system/user/typing.d.ts +++ b/src/services/system/user/typing.d.ts @@ -27,6 +27,7 @@ declare namespace User { package?: Package.PackageItem; UserProfile?: UserProfile; + Order?: Order.OrderItem[]; }; type UserItemByInfo = { @@ -35,5 +36,6 @@ declare namespace User { projectCount: number; projectCount: number; materialsGroup: { _count: number; materialType: string }[]; + dilatationLogs: { id: string; type: string; size: string; desc: string; createDate: string }[]; }; } diff --git a/src/utils/const.ts b/src/utils/const.ts new file mode 100644 index 0000000..63917a6 --- /dev/null +++ b/src/utils/const.ts @@ -0,0 +1,16 @@ +// 订单状态: 1: 待支付 2. 已支付 3. 订单取消 4. 支付失败 5. 订单超时支付(二维码过期) +export const UxOrderStatus: Record = { + 1: '待支付', + 2: '已支付', + 3: '订单取消', + 4: '支付失败', + 5: '订单超时支付(二维码过期)', +}; + +export const UxOrderStatusTag: Record = { + 1: '#3498db', // 蓝色 + 2: '#27ae60', // 绿色 + 3: '#c8d6e5', // 灰色 + 4: '#e74c3c', // 红色 + 5: '订单超时支付(二维码过期)', // +}; diff --git a/src/utils/format.ts b/src/utils/format.ts index fa5ee0a..6191520 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -1,4 +1,11 @@ +// @ts-ignore +import dayjs from 'dayjs'; + // 示例方法,没有实际意义 export function trim(str: string) { return str.trim(); } + +export const formatDateTime = (time: string) => { + return dayjs(time).format('YYYY-MM-DD HH:ss:mm'); +};