feat: 高级列表
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -47,7 +47,7 @@ export async function getInitialState(): Promise<{
|
|||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Error("No token found");
|
throw new Error("No token found");
|
||||||
}
|
}
|
||||||
const { data } = await getInfo();
|
const data = await getInfo();
|
||||||
wsCache.set(CACHE_KEY.USER, data);
|
wsCache.set(CACHE_KEY.USER, data);
|
||||||
wsCache.set(CACHE_KEY.ROLE_ROUTERS, data.menus);
|
wsCache.set(CACHE_KEY.ROLE_ROUTERS, data.menus);
|
||||||
|
|
||||||
@@ -71,13 +71,14 @@ export async function getInitialState(): Promise<{
|
|||||||
if (getAccessToken() && !currentUser) {
|
if (getAccessToken() && !currentUser) {
|
||||||
fetchUserInfo();
|
fetchUserInfo();
|
||||||
}
|
}
|
||||||
|
console.log(111);
|
||||||
return {
|
return {
|
||||||
fetchUserInfo,
|
fetchUserInfo,
|
||||||
currentUser,
|
currentUser,
|
||||||
settings: defaultSettings as Partial<LayoutSettings>,
|
settings: defaultSettings as Partial<LayoutSettings>,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fetchUserInfo,
|
fetchUserInfo,
|
||||||
settings: defaultSettings as Partial<LayoutSettings>,
|
settings: defaultSettings as Partial<LayoutSettings>,
|
||||||
@@ -183,7 +184,6 @@ export const request: RequestConfig = {
|
|||||||
// 获取存储在本地的 token 和 tenantId
|
// 获取存储在本地的 token 和 tenantId
|
||||||
const token = getAccessToken();
|
const token = getAccessToken();
|
||||||
const tenantId = getTenantId(); // 默认租户ID为1
|
const tenantId = getTenantId(); // 默认租户ID为1
|
||||||
console.log(tenantId);
|
|
||||||
// 设置统一的请求头
|
// 设置统一的请求头
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
...options.headers,
|
...options.headers,
|
||||||
@@ -201,6 +201,7 @@ export const request: RequestConfig = {
|
|||||||
// umi 4 使用 modifyRoutes
|
// umi 4 使用 modifyRoutes
|
||||||
export function patchClientRoutes({ routes }: { routes: any }) {
|
export function patchClientRoutes({ routes }: { routes: any }) {
|
||||||
const { wsCache } = useCache();
|
const { wsCache } = useCache();
|
||||||
|
console.log(2222);
|
||||||
const globalMenus = wsCache.get(CACHE_KEY.ROLE_ROUTERS);
|
const globalMenus = wsCache.get(CACHE_KEY.ROLE_ROUTERS);
|
||||||
const routerIndex = routes.findIndex((item: any) => item.path === "/");
|
const routerIndex = routes.findIndex((item: any) => item.path === "/");
|
||||||
const parentId = routes[routerIndex].id;
|
const parentId = routes[routerIndex].id;
|
||||||
|
|||||||
294
src/components/EnhancedProTable/index.tsx
Normal file
294
src/components/EnhancedProTable/index.tsx
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
// components/EnhancedProTable/EnhancedProTable.tsx
|
||||||
|
import React, { useRef, useState, useCallback, useMemo } from "react";
|
||||||
|
import {
|
||||||
|
ProTable,
|
||||||
|
ProColumns,
|
||||||
|
ActionType,
|
||||||
|
TableDropdown,
|
||||||
|
ParamsType,
|
||||||
|
} from "@ant-design/pro-components";
|
||||||
|
import { Button, Space } from "antd";
|
||||||
|
import {
|
||||||
|
EnhancedProTableProps,
|
||||||
|
BaseRecord,
|
||||||
|
TableAction,
|
||||||
|
ToolbarAction,
|
||||||
|
} from "./types";
|
||||||
|
import {
|
||||||
|
buildTableDropdownMenuItems,
|
||||||
|
handleTableDropdownSelect,
|
||||||
|
formatPaginationTotal,
|
||||||
|
} from "@/utils/antd/tableHelpers";
|
||||||
|
|
||||||
|
function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
||||||
|
props: EnhancedProTableProps<T, U>
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
columns: originalColumns,
|
||||||
|
request,
|
||||||
|
actions = [],
|
||||||
|
toolbarActions = [],
|
||||||
|
permissions = [],
|
||||||
|
checkPermission = () => true,
|
||||||
|
showIndex = true,
|
||||||
|
showSelection = true,
|
||||||
|
showActions = true,
|
||||||
|
maxActionCount = 2,
|
||||||
|
onAdd,
|
||||||
|
onEdit,
|
||||||
|
onDelete,
|
||||||
|
onView,
|
||||||
|
onExport,
|
||||||
|
customToolbarRender,
|
||||||
|
customActionRender,
|
||||||
|
...restProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const actionRef = useRef<ActionType>(null);
|
||||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
|
const [selectedRows, setSelectedRows] = useState<T[]>([]);
|
||||||
|
|
||||||
|
// 权限检查
|
||||||
|
const hasPermission = useCallback(
|
||||||
|
(permission?: string) => {
|
||||||
|
if (!permission) return true;
|
||||||
|
return checkPermission(permission);
|
||||||
|
},
|
||||||
|
[checkPermission]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 过滤有权限的操作
|
||||||
|
const filteredActions = useMemo(() => {
|
||||||
|
return actions.filter((action) => hasPermission(action.permission));
|
||||||
|
}, [actions, hasPermission]);
|
||||||
|
|
||||||
|
const filteredToolbarActions = useMemo(() => {
|
||||||
|
return toolbarActions.filter((action) => hasPermission(action.permission));
|
||||||
|
}, [toolbarActions, hasPermission]);
|
||||||
|
|
||||||
|
// 构建操作列
|
||||||
|
const buildActionColumn = useCallback((): ProColumns<T> => {
|
||||||
|
if (!showActions || filteredActions.length === 0) {
|
||||||
|
return {} as ProColumns<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: "操作",
|
||||||
|
valueType: "option",
|
||||||
|
key: "actions",
|
||||||
|
fixed: "right",
|
||||||
|
width: Math.min(filteredActions.length * 60 + 50, 200),
|
||||||
|
render: (_, record, index, action) => {
|
||||||
|
// 过滤可见的操作
|
||||||
|
const visibleActions = filteredActions.filter(
|
||||||
|
(actionItem) => !actionItem.visible || actionItem.visible(record)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (visibleActions.length === 0) return null;
|
||||||
|
|
||||||
|
// 显示的操作按钮
|
||||||
|
const displayActions = visibleActions.slice(0, maxActionCount);
|
||||||
|
// 更多操作
|
||||||
|
const moreActions = visibleActions.slice(maxActionCount);
|
||||||
|
|
||||||
|
const actionElements: React.ReactNode[] = displayActions.map(
|
||||||
|
(actionItem) => (
|
||||||
|
<a
|
||||||
|
key={actionItem.key}
|
||||||
|
onClick={() => actionItem.onClick(record, action)}
|
||||||
|
style={{
|
||||||
|
color: actionItem.danger ? "#ff4d4f" : undefined,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{actionItem.icon} {actionItem.label}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 如果有更多操作,添加下拉菜单
|
||||||
|
if (moreActions.length > 0) {
|
||||||
|
const menuItems = buildTableDropdownMenuItems(
|
||||||
|
moreActions.map((actionItem) => ({
|
||||||
|
key: actionItem.key,
|
||||||
|
label: actionItem.label,
|
||||||
|
icon: actionItem.icon,
|
||||||
|
danger: actionItem.danger,
|
||||||
|
disabled: actionItem.disabled?.(record) || false,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
actionElements.push(
|
||||||
|
<TableDropdown
|
||||||
|
key="more"
|
||||||
|
onSelect={(key) => {
|
||||||
|
handleTableDropdownSelect(key, moreActions, record, action);
|
||||||
|
action?.reload();
|
||||||
|
}}
|
||||||
|
menus={menuItems}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义操作渲染
|
||||||
|
if (customActionRender) {
|
||||||
|
return customActionRender(record, actionElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actionElements;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}, [showActions, filteredActions, maxActionCount, customActionRender]);
|
||||||
|
|
||||||
|
// 构建列配置
|
||||||
|
const enhancedColumns = useMemo(() => {
|
||||||
|
const columns: ProColumns<T>[] = [];
|
||||||
|
|
||||||
|
// 添加序号列
|
||||||
|
if (showIndex) {
|
||||||
|
columns.push({
|
||||||
|
title: "序号",
|
||||||
|
dataIndex: "index",
|
||||||
|
valueType: "indexBorder",
|
||||||
|
width: 48,
|
||||||
|
fixed: "left",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加原始列
|
||||||
|
columns.push(...originalColumns);
|
||||||
|
|
||||||
|
// 添加操作列
|
||||||
|
const actionColumn = buildActionColumn();
|
||||||
|
if (actionColumn.title) {
|
||||||
|
columns.push(actionColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
}, [originalColumns, showIndex, buildActionColumn]);
|
||||||
|
|
||||||
|
// 工具栏渲染
|
||||||
|
const toolBarRender = useCallback(() => {
|
||||||
|
const defaultActions = filteredToolbarActions.map((action) => (
|
||||||
|
<Button
|
||||||
|
key={action.key}
|
||||||
|
type={action.type}
|
||||||
|
danger={action.danger}
|
||||||
|
icon={action.icon}
|
||||||
|
disabled={
|
||||||
|
action.disabled ||
|
||||||
|
(action.needSelection && selectedRowKeys.length === 0)
|
||||||
|
}
|
||||||
|
onClick={() =>
|
||||||
|
action.onClick(selectedRowKeys as number[], selectedRows)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{action.label}
|
||||||
|
</Button>
|
||||||
|
));
|
||||||
|
|
||||||
|
if (customToolbarRender) {
|
||||||
|
return customToolbarRender(defaultActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultActions;
|
||||||
|
}, [
|
||||||
|
filteredToolbarActions,
|
||||||
|
selectedRowKeys,
|
||||||
|
selectedRows,
|
||||||
|
customToolbarRender,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 行选择配置
|
||||||
|
const rowSelection = useMemo(() => {
|
||||||
|
if (!showSelection) return undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectedRowKeys,
|
||||||
|
onChange: (keys: React.Key[], rows: T[]) => {
|
||||||
|
setSelectedRowKeys(keys);
|
||||||
|
setSelectedRows(rows);
|
||||||
|
},
|
||||||
|
getCheckboxProps: (record: T) => ({
|
||||||
|
name: record.id?.toString(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}, [showSelection, selectedRowKeys]);
|
||||||
|
|
||||||
|
// 表格提醒渲染
|
||||||
|
const tableAlertRender = useCallback(
|
||||||
|
({ selectedRowKeys, onCleanSelected }: any) => {
|
||||||
|
if (!showSelection || selectedRowKeys.length === 0) return false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Space size={24}>
|
||||||
|
<span>
|
||||||
|
已选 {selectedRowKeys.length} 项
|
||||||
|
<a style={{ marginLeft: 8 }} onClick={onCleanSelected}>
|
||||||
|
取消选择
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[showSelection]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 表格提醒操作渲染
|
||||||
|
const tableAlertOptionRender = useCallback(() => {
|
||||||
|
if (!showSelection || selectedRowKeys.length === 0) return false;
|
||||||
|
|
||||||
|
const batchActions = filteredToolbarActions.filter(
|
||||||
|
(action) => action.needSelection
|
||||||
|
);
|
||||||
|
|
||||||
|
if (batchActions.length === 0) return false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Space size={16}>
|
||||||
|
{batchActions.map((action) => (
|
||||||
|
<a
|
||||||
|
key={action.key}
|
||||||
|
onClick={() =>
|
||||||
|
action.onClick(selectedRowKeys as number[], selectedRows)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{action.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}, [showSelection, selectedRowKeys, selectedRows, filteredToolbarActions]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProTable<T, U>
|
||||||
|
{...restProps}
|
||||||
|
columns={enhancedColumns}
|
||||||
|
actionRef={actionRef}
|
||||||
|
request={request}
|
||||||
|
rowKey="id"
|
||||||
|
rowSelection={rowSelection}
|
||||||
|
toolBarRender={toolBarRender}
|
||||||
|
tableAlertRender={tableAlertRender}
|
||||||
|
tableAlertOptionRender={tableAlertOptionRender}
|
||||||
|
search={{
|
||||||
|
labelWidth: "auto",
|
||||||
|
...restProps.search,
|
||||||
|
}}
|
||||||
|
options={{
|
||||||
|
fullScreen: true,
|
||||||
|
reload: true,
|
||||||
|
setting: true,
|
||||||
|
density: true,
|
||||||
|
...restProps.options,
|
||||||
|
}}
|
||||||
|
pagination={{
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: formatPaginationTotal,
|
||||||
|
...restProps.pagination,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EnhancedProTable;
|
||||||
113
src/components/EnhancedProTable/types.ts
Normal file
113
src/components/EnhancedProTable/types.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// components/EnhancedProTable/types.ts
|
||||||
|
import {
|
||||||
|
ProTableProps,
|
||||||
|
ProColumns,
|
||||||
|
ActionType,
|
||||||
|
} from "@ant-design/pro-components";
|
||||||
|
import { ButtonProps } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export interface BaseRecord {
|
||||||
|
id: number;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiResponse<T> {
|
||||||
|
data: T[];
|
||||||
|
total: number;
|
||||||
|
success: boolean;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableDropdownMenuItem {
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
label?: React.ReactNode;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
danger?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableAction<T = any> {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
type?: ButtonProps["type"];
|
||||||
|
danger?: boolean;
|
||||||
|
disabled?: (record: T) => boolean;
|
||||||
|
visible?: (record: T) => boolean;
|
||||||
|
onClick: (record: T, action?: ActionType) => void;
|
||||||
|
permission?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ToolbarAction {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
type?: ButtonProps["type"];
|
||||||
|
danger?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
onClick: (selectedKeys: React.Key[], selectedRows: any[]) => void;
|
||||||
|
permission?: string;
|
||||||
|
needSelection?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnhancedProTableProps<T extends BaseRecord, U = any>
|
||||||
|
extends Omit<ProTableProps<T, U>, "columns" | "request" | "toolBarRender"> {
|
||||||
|
columns: ProColumns<T>[];
|
||||||
|
request?: (
|
||||||
|
params: U & { current?: number; pageSize?: number }
|
||||||
|
) => Promise<ApiResponse<T>>;
|
||||||
|
actions?: TableAction<T>[];
|
||||||
|
toolbarActions?: ToolbarAction[];
|
||||||
|
permissions?: string[];
|
||||||
|
checkPermission?: (permission: string) => boolean;
|
||||||
|
showIndex?: boolean;
|
||||||
|
showSelection?: boolean;
|
||||||
|
showActions?: boolean;
|
||||||
|
maxActionCount?: number;
|
||||||
|
onAdd?: () => void;
|
||||||
|
onEdit?: (record: T) => void;
|
||||||
|
onDelete?: (record: T) => void;
|
||||||
|
onView?: (record: T) => void;
|
||||||
|
onExport?: (selectedRows: T[]) => void;
|
||||||
|
customToolbarRender?: (
|
||||||
|
defaultActions: React.ReactNode[]
|
||||||
|
) => React.ReactNode[];
|
||||||
|
customActionRender?: (
|
||||||
|
record: T,
|
||||||
|
defaultActions: React.ReactNode[]
|
||||||
|
) => React.ReactNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UseEnhancedTableOptions<T> {
|
||||||
|
onAdd?: () => void;
|
||||||
|
onEdit?: (record: T) => void;
|
||||||
|
onDelete?: (record: T) => Promise<boolean>;
|
||||||
|
onView?: (record: T) => void;
|
||||||
|
onExport?: (selectedRows: T[]) => void;
|
||||||
|
onBatchDelete?: (selectedKeys: React.Key[]) => Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UseEnhancedTableReturn<T> {
|
||||||
|
actionRef: React.MutableRefObject<ActionType | undefined>;
|
||||||
|
selectedRowKeys: React.Key[];
|
||||||
|
selectedRows: T[];
|
||||||
|
loading: boolean;
|
||||||
|
setSelectedRowKeys: (keys: React.Key[]) => void;
|
||||||
|
setSelectedRows: (rows: T[]) => void;
|
||||||
|
reload: () => void;
|
||||||
|
reset: () => void;
|
||||||
|
clearSelection: () => void;
|
||||||
|
handleDelete: (record: T) => Promise<void>;
|
||||||
|
handleBatchDelete: (keys: React.Key[]) => Promise<void>;
|
||||||
|
handleExport: (rows: T[]) => void;
|
||||||
|
actions: {
|
||||||
|
add?: () => void;
|
||||||
|
edit?: (record: T) => void;
|
||||||
|
delete: (record: T) => Promise<void>;
|
||||||
|
view?: (record: T) => void;
|
||||||
|
export: (rows: T[]) => void;
|
||||||
|
batchDelete: (keys: React.Key[]) => Promise<void>;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,31 +1,31 @@
|
|||||||
import { GithubOutlined } from '@ant-design/icons';
|
import { GithubOutlined } from "@ant-design/icons";
|
||||||
import { DefaultFooter } from '@ant-design/pro-components';
|
import { DefaultFooter } from "@ant-design/pro-components";
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
|
|
||||||
const Footer: React.FC = () => {
|
const Footer: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<DefaultFooter
|
<DefaultFooter
|
||||||
style={{
|
style={{
|
||||||
background: 'none',
|
background: "none",
|
||||||
}}
|
}}
|
||||||
copyright="Powered by Ant Desgin"
|
copyright="20250923"
|
||||||
links={[
|
links={[
|
||||||
{
|
{
|
||||||
key: 'Ant Design Pro',
|
key: "by",
|
||||||
title: 'Ant Design Pro',
|
title: "百业到家",
|
||||||
href: 'https://pro.ant.design',
|
href: "/welcome",
|
||||||
blankTarget: true,
|
blankTarget: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'github',
|
key: "github",
|
||||||
title: <GithubOutlined />,
|
title: <GithubOutlined />,
|
||||||
href: 'https://github.com/ant-design/ant-design-pro',
|
href: "https://github.com/ant-design/ant-design-pro",
|
||||||
blankTarget: true,
|
blankTarget: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Ant Design',
|
key: "Ant Design",
|
||||||
title: 'Ant Design',
|
title: "Ant Design",
|
||||||
href: 'https://ant.design',
|
href: "https://ant.design",
|
||||||
blankTarget: true,
|
blankTarget: true,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
34
src/constants/icons.tsx
Normal file
34
src/constants/icons.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// constants/icons.tsx
|
||||||
|
import {
|
||||||
|
EditOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
EyeOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
ExportOutlined,
|
||||||
|
DownloadOutlined,
|
||||||
|
UploadOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
SettingOutlined,
|
||||||
|
MoreOutlined,
|
||||||
|
CopyOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
|
||||||
|
export const ICONS = {
|
||||||
|
edit: <EditOutlined />,
|
||||||
|
delete: <DeleteOutlined />,
|
||||||
|
view: <EyeOutlined />,
|
||||||
|
add: <PlusOutlined />,
|
||||||
|
export: <ExportOutlined />,
|
||||||
|
download: <DownloadOutlined />,
|
||||||
|
upload: <UploadOutlined />,
|
||||||
|
search: <SearchOutlined />,
|
||||||
|
reload: <ReloadOutlined />,
|
||||||
|
setting: <SettingOutlined />,
|
||||||
|
more: <MoreOutlined />,
|
||||||
|
copy: <CopyOutlined />,
|
||||||
|
user: <UserOutlined />,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type IconType = keyof typeof ICONS;
|
||||||
116
src/hooks/antd/useEnhancedTable.ts
Normal file
116
src/hooks/antd/useEnhancedTable.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
// hooks/useEnhancedTable.ts
|
||||||
|
import { useRef, useState, useCallback } from "react";
|
||||||
|
import { ActionType } from "@ant-design/pro-components";
|
||||||
|
import { message } from "antd";
|
||||||
|
import {
|
||||||
|
UseEnhancedTableOptions,
|
||||||
|
UseEnhancedTableReturn,
|
||||||
|
} from "@/components/EnhancedProTable/types";
|
||||||
|
|
||||||
|
export function useEnhancedTable<T>(
|
||||||
|
options: UseEnhancedTableOptions<T> = {}
|
||||||
|
): UseEnhancedTableReturn<T> {
|
||||||
|
const actionRef = useRef<ActionType>(undefined);
|
||||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
|
const [selectedRows, setSelectedRows] = useState<T[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const { onAdd, onEdit, onDelete, onView, onExport, onBatchDelete } = options;
|
||||||
|
|
||||||
|
// 刷新表格
|
||||||
|
const reload = useCallback(() => {
|
||||||
|
actionRef.current?.reload();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 重置表格
|
||||||
|
const reset = useCallback(() => {
|
||||||
|
actionRef.current?.reset?.();
|
||||||
|
setSelectedRowKeys([]);
|
||||||
|
setSelectedRows([]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 清空选择
|
||||||
|
const clearSelection = useCallback(() => {
|
||||||
|
setSelectedRowKeys([]);
|
||||||
|
setSelectedRows([]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 处理删除
|
||||||
|
const handleDelete = useCallback(
|
||||||
|
async (record: T) => {
|
||||||
|
if (!onDelete) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const success = await onDelete(record);
|
||||||
|
if (success) {
|
||||||
|
message.success("删除成功");
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onDelete, reload]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 处理批量删除
|
||||||
|
const handleBatchDelete = useCallback(
|
||||||
|
async (keys: React.Key[]) => {
|
||||||
|
if (!onBatchDelete || keys.length === 0) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const success = await onBatchDelete(keys);
|
||||||
|
if (success) {
|
||||||
|
message.success("批量删除成功");
|
||||||
|
clearSelection();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.error("批量删除失败");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onBatchDelete, clearSelection, reload]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 处理导出
|
||||||
|
const handleExport = useCallback(
|
||||||
|
(rows: T[]) => {
|
||||||
|
if (!onExport) return;
|
||||||
|
if (rows.length === 0) {
|
||||||
|
message.warning("请选择要导出的数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onExport(rows);
|
||||||
|
},
|
||||||
|
[onExport]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actionRef,
|
||||||
|
selectedRowKeys,
|
||||||
|
selectedRows,
|
||||||
|
loading,
|
||||||
|
setSelectedRowKeys,
|
||||||
|
setSelectedRows,
|
||||||
|
reload,
|
||||||
|
reset,
|
||||||
|
clearSelection,
|
||||||
|
handleDelete,
|
||||||
|
handleBatchDelete,
|
||||||
|
handleExport,
|
||||||
|
actions: {
|
||||||
|
add: onAdd,
|
||||||
|
edit: onEdit,
|
||||||
|
delete: handleDelete,
|
||||||
|
view: onView,
|
||||||
|
export: handleExport,
|
||||||
|
batchDelete: handleBatchDelete,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { UseEnhancedTableOptions, UseEnhancedTableReturn };
|
||||||
@@ -1,5 +1,109 @@
|
|||||||
|
import EnhancedProTable from "@/components/EnhancedProTable";
|
||||||
|
import {
|
||||||
|
getTenantPage,
|
||||||
|
deleteTenant,
|
||||||
|
type TenantPageReqVO,
|
||||||
|
type TenantVO,
|
||||||
|
} from "@/services/system/tenant/list";
|
||||||
|
import { PlusOutlined } from "@ant-design/icons";
|
||||||
|
import {
|
||||||
|
ProCoreActionType,
|
||||||
|
ProTable,
|
||||||
|
TableDropdown,
|
||||||
|
type ProColumns,
|
||||||
|
} from "@ant-design/pro-components";
|
||||||
|
import { Button, Space } from "antd";
|
||||||
|
|
||||||
|
import {
|
||||||
|
createOrderTableConfig,
|
||||||
|
createCommonActions,
|
||||||
|
createCommonToolbarActions,
|
||||||
|
} from "@/utils/antd/tableConfigFactory";
|
||||||
|
import { useEnhancedTable } from "@/hooks/antd/useEnhancedTable";
|
||||||
const TenantList = () => {
|
const TenantList = () => {
|
||||||
return <div>TenantList</div>;
|
const columns: ProColumns<TenantVO>[] = [
|
||||||
|
{
|
||||||
|
title: "租户编号",
|
||||||
|
dataIndex: "id",
|
||||||
|
tip: "租户编号",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "租户名",
|
||||||
|
dataIndex: "name",
|
||||||
|
|
||||||
|
ellipsis: true, // 超长省略
|
||||||
|
tip: "租户名", // 提示信息
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "状态",
|
||||||
|
dataIndex: "status",
|
||||||
|
valueType: "select",
|
||||||
|
valueEnum: {
|
||||||
|
all: { text: "全部", status: "Default" },
|
||||||
|
open: { text: "未解决", status: "Error" },
|
||||||
|
closed: { text: "已解决", status: "Success" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "创建时间",
|
||||||
|
dataIndex: "created_at",
|
||||||
|
valueType: "dateTime",
|
||||||
|
hideInSearch: true, // 在搜索表单中隐藏
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const onFetch = async (
|
||||||
|
params: TenantPageReqVO & {
|
||||||
|
pageSize?: number;
|
||||||
|
current?: number;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const data = await getTenantPage({
|
||||||
|
pageNo: params.current,
|
||||||
|
pageSize: params.pageSize,
|
||||||
|
});
|
||||||
|
console.log(data);
|
||||||
|
return {
|
||||||
|
data: data.list,
|
||||||
|
success: true,
|
||||||
|
total: data.total,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const { actionRef, selectedRowKeys, selectedRows, actions } =
|
||||||
|
useEnhancedTable<TenantVO>({
|
||||||
|
onEdit: (record) => {
|
||||||
|
console.log("编辑订单:", record);
|
||||||
|
},
|
||||||
|
onDelete: async (record) => {
|
||||||
|
await deleteTenant(record.id);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const tableActions = createCommonActions<TenantVO>({
|
||||||
|
onView: actions.view,
|
||||||
|
onEdit: actions.edit,
|
||||||
|
onDelete: actions.delete,
|
||||||
|
});
|
||||||
|
|
||||||
|
const toolbarActions = createCommonToolbarActions({
|
||||||
|
onExport: actions.export,
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<EnhancedProTable<TenantVO>
|
||||||
|
columns={columns}
|
||||||
|
request={onFetch}
|
||||||
|
actions={tableActions}
|
||||||
|
toolbarActions={toolbarActions}
|
||||||
|
headerTitle="订单管理"
|
||||||
|
showIndex={false}
|
||||||
|
// showSelection
|
||||||
|
showActions
|
||||||
|
maxActionCount={3}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TenantList;
|
export default TenantList;
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ const Page = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
// 获取租户 ID
|
// 获取租户 ID
|
||||||
const getTenantId = async (name: string) => {
|
const getTenantId = async (name: string) => {
|
||||||
const res = await getTenantIdByName({ name });
|
const data = await getTenantIdByName({ name });
|
||||||
authUtil.setTenantId(res.data);
|
authUtil.setTenantId(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchUserInfo = async () => {
|
const fetchUserInfo = async () => {
|
||||||
@@ -69,19 +69,14 @@ const Page = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 调用登录接口
|
// 调用登录接口
|
||||||
const res = await login(params);
|
const data = await login(params);
|
||||||
if (res.code === 0) {
|
|
||||||
// 登录成功
|
// 登录成功
|
||||||
messageApi.success("登录成功!");
|
messageApi.success("登录成功!");
|
||||||
// 跳转到首页
|
// 跳转到首页
|
||||||
authUtil.setToken(res.data);
|
authUtil.setToken(data);
|
||||||
await fetchUserInfo();
|
await fetchUserInfo();
|
||||||
const urlParams = new URL(window.location.href).searchParams;
|
const urlParams = new URL(window.location.href).searchParams;
|
||||||
navigate(urlParams.get("redirect") || "/");
|
navigate(urlParams.get("redirect") || "/");
|
||||||
} else {
|
|
||||||
// 登录失败
|
|
||||||
messageApi.error(res.msg || "登录失败,请重试!");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
messageApi.error("登录失败,请检查网络或稍后重试!");
|
messageApi.error("登录失败,请检查网络或稍后重试!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,13 @@
|
|||||||
import type { RequestConfig } from "@umijs/max";
|
import type { RequestConfig } from "@umijs/max";
|
||||||
import { message, notification } from "antd";
|
import { message, notification } from "antd";
|
||||||
import { deleteUserCache } from "@/hooks/web/useCache";
|
import { deleteUserCache } from "@/hooks/web/useCache";
|
||||||
|
import {
|
||||||
|
getAccessToken,
|
||||||
|
getRefreshToken,
|
||||||
|
getTenantId,
|
||||||
|
setToken,
|
||||||
|
} from "./utils/auth";
|
||||||
|
import { request } from "@umijs/max";
|
||||||
const tenantEnable = process.env.VITE_APP_TENANT_ENABLE;
|
const tenantEnable = process.env.VITE_APP_TENANT_ENABLE;
|
||||||
// const { result_code, base_url, request_timeout } = config;
|
// const { result_code, base_url, request_timeout } = config;
|
||||||
// 错误处理方案: 错误类型
|
// 错误处理方案: 错误类型
|
||||||
@@ -16,9 +23,8 @@ enum ErrorShowType {
|
|||||||
interface ResponseStructure {
|
interface ResponseStructure {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
data: any;
|
data: any;
|
||||||
errorCode?: number;
|
code?: number;
|
||||||
errorMessage?: string;
|
msg?: string;
|
||||||
showType?: ErrorShowType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,63 +32,76 @@ interface ResponseStructure {
|
|||||||
* pro 自带的错误处理, 可以在这里做自己的改动
|
* pro 自带的错误处理, 可以在这里做自己的改动
|
||||||
* @doc https://umijs.org/docs/max/request#配置
|
* @doc https://umijs.org/docs/max/request#配置
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const refreshToken = async () => {
|
||||||
|
return await request("/system/auth/refresh-token", {
|
||||||
|
method: "POST",
|
||||||
|
params: { refreshToken: getRefreshToken() },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAuthorized = () => {
|
||||||
|
// const { t } = useI18n()
|
||||||
|
// if (!isRelogin.show) {
|
||||||
|
// // 如果已经到登录页面则不进行弹窗提示
|
||||||
|
// if (window.location.href.includes('login')) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// isRelogin.show = true
|
||||||
|
// ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), {
|
||||||
|
// showCancelButton: false,
|
||||||
|
// closeOnClickModal: false,
|
||||||
|
// showClose: false,
|
||||||
|
// closeOnPressEscape: false,
|
||||||
|
// confirmButtonText: t('login.relogin'),
|
||||||
|
// type: 'warning'
|
||||||
|
// }).then(() => {
|
||||||
|
// resetRouter() // 重置静态路由表
|
||||||
|
// deleteUserCache() // 删除用户缓存
|
||||||
|
// removeToken()
|
||||||
|
// isRelogin.show = false
|
||||||
|
// // 干掉token后再走一次路由让它过router.beforeEach的校验
|
||||||
|
// window.location.href = window.location.href
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// return Promise.reject(t('sys.api.timeoutMessage'))
|
||||||
|
};
|
||||||
|
// 是否正在刷新中
|
||||||
|
let isRefreshToken = false;
|
||||||
|
// 请求队列
|
||||||
|
let requestList: any[] = [];
|
||||||
export const errorConfig: RequestConfig = {
|
export const errorConfig: RequestConfig = {
|
||||||
// 错误处理: umi@3 的错误处理方案。
|
// 错误处理: umi@3 的错误处理方案。
|
||||||
errorConfig: {
|
errorConfig: {
|
||||||
// 错误抛出
|
// 错误抛出
|
||||||
errorThrower: (res) => {
|
errorThrower: (res) => {
|
||||||
const { success, data, errorCode, errorMessage, showType } =
|
const { success, data, code, msg } = res as unknown as ResponseStructure;
|
||||||
res as unknown as ResponseStructure;
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
const error: any = new Error(errorMessage);
|
const error: any = new Error(msg);
|
||||||
error.name = "BizError";
|
error.name = "BizError";
|
||||||
error.info = { errorCode, errorMessage, showType, data };
|
error.info = { code, msg, data };
|
||||||
throw error; // 抛出自制的错误
|
throw error; // 抛出自制的错误
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 错误接收及处理
|
// 错误接收及处理
|
||||||
errorHandler: (error: any, opts: any) => {
|
errorHandler: async (error: any, opts: any) => {
|
||||||
if (opts?.skipErrorHandler) throw error;
|
if (opts?.skipErrorHandler) throw error;
|
||||||
// 我们的 errorThrower 抛出的错误。
|
// 我们的 errorThrower 抛出的错误。
|
||||||
if (error.name === "BizError") {
|
console.log("errorHandler", error);
|
||||||
const errorInfo: ResponseStructure | undefined = error.info;
|
const errorInfo: ResponseStructure | undefined = error.info;
|
||||||
|
if (error.name === "BizError") {
|
||||||
if (errorInfo) {
|
if (errorInfo) {
|
||||||
const { errorMessage, errorCode } = errorInfo;
|
const { msg, code } = errorInfo;
|
||||||
switch (errorInfo.showType) {
|
message.error(msg);
|
||||||
case ErrorShowType.SILENT:
|
|
||||||
// do nothing
|
|
||||||
break;
|
|
||||||
case ErrorShowType.WARN_MESSAGE:
|
|
||||||
message.warning(errorMessage);
|
|
||||||
break;
|
|
||||||
case ErrorShowType.ERROR_MESSAGE:
|
|
||||||
message.error(errorMessage);
|
|
||||||
break;
|
|
||||||
case ErrorShowType.NOTIFICATION:
|
|
||||||
notification.open({
|
|
||||||
description: errorMessage,
|
|
||||||
message: errorCode,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case ErrorShowType.REDIRECT:
|
|
||||||
// TODO: redirect
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
message.error(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (error.response) {
|
} else if (error.response) {
|
||||||
// Axios 的错误
|
// Axios 的错误
|
||||||
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
|
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
|
||||||
message.error(`Response status:${error.response.status}`);
|
message.error(`Response status:${error.response.status}`);
|
||||||
} else if (error.request) {
|
} else if (error.request) {
|
||||||
// 请求已经成功发起,但没有收到响应
|
|
||||||
// \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
|
|
||||||
// 而在node.js中是 http.ClientRequest 的实例
|
|
||||||
message.error("None response! Please retry.");
|
message.error("None response! Please retry.");
|
||||||
} else {
|
} else {
|
||||||
// 发送请求时出了点问题
|
message.error("发送请求时出了点问题:" + error.msg);
|
||||||
message.error("Request error, please retry.");
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -98,14 +117,61 @@ export const errorConfig: RequestConfig = {
|
|||||||
|
|
||||||
// 响应拦截器
|
// 响应拦截器
|
||||||
responseInterceptors: [
|
responseInterceptors: [
|
||||||
(response) => {
|
async (response) => {
|
||||||
// 拦截响应数据,进行个性化处理
|
|
||||||
const { data } = response as unknown as ResponseStructure;
|
const { data } = response as unknown as ResponseStructure;
|
||||||
|
const config = response.config;
|
||||||
|
const { code } = data;
|
||||||
|
// 发送请求时出了点问题
|
||||||
|
if (code === 401) {
|
||||||
|
if (!isRefreshToken) {
|
||||||
|
isRefreshToken = true;
|
||||||
|
// 1. 如果获取不到刷新令牌,则只能执行登出操作
|
||||||
|
if (!getRefreshToken()) {
|
||||||
|
return handleAuthorized();
|
||||||
|
}
|
||||||
|
// 2. 进行刷新访问令牌
|
||||||
|
try {
|
||||||
|
const refreshTokenRes = await refreshToken();
|
||||||
|
console.log("刷新成功", refreshTokenRes);
|
||||||
|
// 2.1 刷新成功,则回放队列的请求 + 当前请求
|
||||||
|
setToken(refreshTokenRes);
|
||||||
|
console.log(getAccessToken());
|
||||||
|
config.headers!.Authorization = "Bearer " + getAccessToken();
|
||||||
|
requestList.forEach((cb: any) => {
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
|
||||||
|
// 2.2 刷新失败,只回放队列的请求
|
||||||
|
requestList.forEach((cb: any) => {
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
// 提示是否要登出。即不回放当前请求!不然会形成递归
|
||||||
|
return handleAuthorized();
|
||||||
|
} finally {
|
||||||
|
requestList = [];
|
||||||
|
isRefreshToken = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
//添加到队列,等待刷新获取到新的令牌
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
requestList.push(() => {
|
||||||
|
config.headers!.Authorization = "Bearer " + getAccessToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||||
|
resolve(request(config.url!, config));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.code !== 0) {
|
||||||
|
return Promise.reject(data);
|
||||||
|
}
|
||||||
if (data?.success === false) {
|
if (data?.success === false) {
|
||||||
message.error("请求失败!");
|
message.error("请求失败!");
|
||||||
|
return Promise.reject(data);
|
||||||
}
|
}
|
||||||
return response;
|
|
||||||
|
return data;
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export async function login(
|
|||||||
body: API.UserLoginVO,
|
body: API.UserLoginVO,
|
||||||
options?: { [key: string]: any }
|
options?: { [key: string]: any }
|
||||||
) {
|
) {
|
||||||
return request<IResponse<API.TokenType>>("/system/auth/login", {
|
return request<API.TokenType>("/system/auth/login", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -84,16 +84,13 @@ export async function loginOut() {
|
|||||||
// return request.get({ url: "/system/auth/get-permission-info" });
|
// return request.get({ url: "/system/auth/get-permission-info" });
|
||||||
// };
|
// };
|
||||||
export async function getInfo(options?: { [key: string]: any }) {
|
export async function getInfo(options?: { [key: string]: any }) {
|
||||||
return request<IResponse<API.UserInfoVO>>(
|
return request<API.UserInfoVO>("/system/auth/get-permission-info", {
|
||||||
"/system/auth/get-permission-info",
|
|
||||||
{
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
//获取登录验证码
|
//获取登录验证码
|
||||||
// export const sendSmsCode = (data: SmsCodeVO) => {
|
// export const sendSmsCode = (data: SmsCodeVO) => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
/* eslint-disable */
|
/// <reference path="./typings.d.ts" />
|
||||||
import { request } from "@umijs/max";
|
import { request } from "@umijs/max";
|
||||||
|
|
||||||
/** 获取菜单页面的表 GET /product/category/categoryList */
|
/** 获取菜单页面的表 GET /product/category/categoryList */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
/* eslint-disable */
|
/// <reference path="./typings.d.ts" />
|
||||||
import { request } from "@umijs/max";
|
import { request } from "@umijs/max";
|
||||||
|
|
||||||
/** 创建商品 创建商品 POST /prod/create */
|
/** 创建商品 创建商品 POST /prod/create */
|
||||||
@@ -88,12 +88,9 @@ export async function getProductProdGetProdRecycleBinPageList(
|
|||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
params: {
|
params: {
|
||||||
// pageNo has a default value: 1
|
|
||||||
pageNo: "1",
|
|
||||||
// pageSize has a default value: 10
|
|
||||||
pageSize: "10",
|
|
||||||
|
|
||||||
...params,
|
...params,
|
||||||
|
pageNo: params.pageNo ?? "1",
|
||||||
|
pageSize: params.pageSize ?? "10",
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
}
|
}
|
||||||
@@ -127,12 +124,9 @@ export async function getProductProdPage(
|
|||||||
return request<API.CommonResultPageResultProdListVO>("/product/prod/page", {
|
return request<API.CommonResultPageResultProdListVO>("/product/prod/page", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
params: {
|
params: {
|
||||||
// pageNo has a default value: 1
|
|
||||||
pageNo: "1",
|
|
||||||
// pageSize has a default value: 10
|
|
||||||
pageSize: "10",
|
|
||||||
|
|
||||||
...params,
|
...params,
|
||||||
|
pageNo: params.pageNo ?? "1",
|
||||||
|
pageSize: params.pageSize ?? "10",
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
@@ -299,12 +293,9 @@ export async function getProductSkuGetPropRecycleBinList(
|
|||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
params: {
|
params: {
|
||||||
// pageNo has a default value: 1
|
|
||||||
pageNo: "1",
|
|
||||||
// pageSize has a default value: 10
|
|
||||||
pageSize: "10",
|
|
||||||
|
|
||||||
...params,
|
...params,
|
||||||
|
pageNo: params.pageNo ?? "1",
|
||||||
|
pageSize: params.pageSize ?? "10",
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
}
|
}
|
||||||
@@ -337,12 +328,9 @@ export async function getProductSkuGetSkuPageList(
|
|||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
params: {
|
params: {
|
||||||
// pageNo has a default value: 1
|
|
||||||
pageNo: "1",
|
|
||||||
// pageSize has a default value: 10
|
|
||||||
pageSize: "10",
|
|
||||||
|
|
||||||
...params,
|
...params,
|
||||||
|
pageNo: params.pageNo ?? "1",
|
||||||
|
pageSize: params.pageSize ?? "10",
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
}
|
}
|
||||||
@@ -375,12 +363,9 @@ export async function getProductSkuGetSkuRecycleBinPageList(
|
|||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
params: {
|
params: {
|
||||||
// pageNo has a default value: 1
|
|
||||||
pageNo: "1",
|
|
||||||
// pageSize has a default value: 10
|
|
||||||
pageSize: "10",
|
|
||||||
|
|
||||||
...params,
|
...params,
|
||||||
|
pageNo: params.pageNo ?? "1",
|
||||||
|
pageSize: params.pageSize ?? "10",
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
}
|
}
|
||||||
|
|||||||
53
src/services/prodApi/typings.d.ts
vendored
53
src/services/prodApi/typings.d.ts
vendored
@@ -202,6 +202,15 @@ declare namespace API {
|
|||||||
msg?: string;
|
msg?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CommonResultPageResultTradeOrderPageItemRespVO = {
|
||||||
|
/** 错误码 */
|
||||||
|
code?: number;
|
||||||
|
/** 返回数据 */
|
||||||
|
data?: PageResultTradeOrderPageItemRespVO;
|
||||||
|
/** 错误提示,用户可阅读 */
|
||||||
|
msg?: string;
|
||||||
|
};
|
||||||
|
|
||||||
type CommonResultProdServiceVO = {
|
type CommonResultProdServiceVO = {
|
||||||
/** 错误码 */
|
/** 错误码 */
|
||||||
code?: number;
|
code?: number;
|
||||||
@@ -371,6 +380,43 @@ declare namespace API {
|
|||||||
skuId?: number;
|
skuId?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type getTradeOrderGetDetailParams = {
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type getTradeOrderPageParams = {
|
||||||
|
/** 页码,从 1 开始", example = "1 */
|
||||||
|
pageNo: number;
|
||||||
|
/** 每页条数,最大值为 100" */
|
||||||
|
pageSize: number;
|
||||||
|
/** 订单号,示例:88888888 */
|
||||||
|
orderNum?: string;
|
||||||
|
/** 用户编号,示例:1024 */
|
||||||
|
userId?: number;
|
||||||
|
/** 用户昵称,示例:小王 */
|
||||||
|
userNickname?: string;
|
||||||
|
/** 用户手机号,示例:小王 */
|
||||||
|
userMobile?: string;
|
||||||
|
/** 配送方式,示例:1 */
|
||||||
|
deliveryType?: number;
|
||||||
|
/** 发货物流公司编号,示例:1 */
|
||||||
|
logisticsId?: number;
|
||||||
|
/** 自提门店编号,示例:[1,2] */
|
||||||
|
pickUpStoreIds?: string;
|
||||||
|
/** 自提核销码,示例:12345678 */
|
||||||
|
pickUpVerifyCode?: string;
|
||||||
|
/** 订单类型,示例:1 */
|
||||||
|
type?: number;
|
||||||
|
/** 订单状态,示例:1 */
|
||||||
|
status?: number;
|
||||||
|
/** 支付渠道,示例:wx_lite */
|
||||||
|
payChannelCode?: string;
|
||||||
|
/** 创建时间 */
|
||||||
|
createTime?: string[];
|
||||||
|
/** 订单来源,示例:10 */
|
||||||
|
terminal?: number;
|
||||||
|
};
|
||||||
|
|
||||||
type Item = {
|
type Item = {
|
||||||
/** 编号 - 必填,示例:1 */
|
/** 编号 - 必填,示例:1 */
|
||||||
id?: number;
|
id?: number;
|
||||||
@@ -497,6 +543,13 @@ declare namespace API {
|
|||||||
total?: number;
|
total?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type PageResultTradeOrderPageItemRespVO = {
|
||||||
|
/** 数据 */
|
||||||
|
list?: TradeOrderPageItemRespVO[];
|
||||||
|
/** 总量 */
|
||||||
|
total?: number;
|
||||||
|
};
|
||||||
|
|
||||||
type postProductProdRestoreProdListParams = {
|
type postProductProdRestoreProdListParams = {
|
||||||
/** 商品id集合 */
|
/** 商品id集合 */
|
||||||
ids: number[];
|
ids: number[];
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export interface PermissionAssignRoleMenuReqVO {
|
|||||||
export interface PermissionAssignRoleDataScopeReqVO {
|
export interface PermissionAssignRoleDataScopeReqVO {
|
||||||
roleId: number;
|
roleId: number;
|
||||||
dataScope: number;
|
dataScope: number;
|
||||||
dataScopeDeptIds: number[];
|
dataScopeDeptIds: React.Key[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// export async function postProductProdRestoreProdList(
|
// export async function postProductProdRestoreProdList(
|
||||||
|
|||||||
91
src/services/system/tenant/list.ts
Normal file
91
src/services/system/tenant/list.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { request } from "@umijs/max";
|
||||||
|
|
||||||
|
export interface TenantVO {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
contactName: string;
|
||||||
|
contactMobile: string;
|
||||||
|
status: number;
|
||||||
|
domain: string;
|
||||||
|
packageId: number;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
expireTime: Date;
|
||||||
|
accountCount: number;
|
||||||
|
createTime: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TenantPageReqVO extends PageParam {
|
||||||
|
name?: string;
|
||||||
|
contactName?: string;
|
||||||
|
contactMobile?: string;
|
||||||
|
status?: number;
|
||||||
|
createTime?: Date[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TenantExportReqVO {
|
||||||
|
name?: string;
|
||||||
|
contactName?: string;
|
||||||
|
contactMobile?: string;
|
||||||
|
status?: number;
|
||||||
|
createTime?: Date[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询租户列表
|
||||||
|
// export const getTenantPage = (params: TenantPageReqVO) => {
|
||||||
|
// return request.get({ url: '/system/tenant/page', params })
|
||||||
|
// }
|
||||||
|
export async function getTenantPage(params: TenantPageReqVO) {
|
||||||
|
return request("/system/tenant/page", {
|
||||||
|
method: "GET",
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 查询租户详情
|
||||||
|
// export const getTenant = (id: number) => {
|
||||||
|
// return request.get({ url: "/system/tenant/get?id=" + id });
|
||||||
|
// };
|
||||||
|
export async function getTenant(id: number) {
|
||||||
|
return request("/system/tenant/get", {
|
||||||
|
method: "GET",
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 新增租户
|
||||||
|
// export const createTenant = (data: TenantVO) => {
|
||||||
|
// return request.post({ url: "/system/tenant/create", data });
|
||||||
|
// };
|
||||||
|
export async function createTenant(data: TenantVO) {
|
||||||
|
return request("/system/tenant/create", {
|
||||||
|
method: "POST",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 修改租户
|
||||||
|
// export const updateTenant = (data: TenantVO) => {
|
||||||
|
// return request.put({ url: "/system/tenant/update", data });
|
||||||
|
// };
|
||||||
|
export async function updateTenant(data: TenantVO) {
|
||||||
|
return request("/system/tenant/update", {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 删除租户
|
||||||
|
// export const deleteTenant = (id: number) => {
|
||||||
|
// return request.delete({ url: "/system/tenant/delete?id=" + id });
|
||||||
|
// };
|
||||||
|
export async function deleteTenant(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
id: number
|
||||||
|
) {
|
||||||
|
return request("/system/tenant/delete", {
|
||||||
|
method: "DELETE",
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
282
src/utils/antd/tableConfigFactory.tsx
Normal file
282
src/utils/antd/tableConfigFactory.tsx
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
// utils/tableConfigFactory.tsx
|
||||||
|
import React from "react";
|
||||||
|
import { ProColumns } from "@ant-design/pro-components";
|
||||||
|
import { Tag } from "antd";
|
||||||
|
import { ICONS, IconType } from "@/constants/icons";
|
||||||
|
import {
|
||||||
|
TableAction,
|
||||||
|
ToolbarAction,
|
||||||
|
} from "@/components/EnhancedProTable/types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建操作配置的辅助函数
|
||||||
|
*/
|
||||||
|
const createAction = <T extends { id: number }>(
|
||||||
|
key: string,
|
||||||
|
label: string,
|
||||||
|
iconType: IconType,
|
||||||
|
onClick: (record: T) => void,
|
||||||
|
options: Partial<TableAction<T>> = {}
|
||||||
|
): TableAction<T> => ({
|
||||||
|
key,
|
||||||
|
label,
|
||||||
|
// icon: ICONS[iconType],
|
||||||
|
onClick,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建工具栏操作的辅助函数
|
||||||
|
*/
|
||||||
|
const createToolbarAction = (
|
||||||
|
key: string,
|
||||||
|
label: string,
|
||||||
|
iconType: IconType,
|
||||||
|
onClick: (selectedKeys: React.Key[], selectedRows: any[]) => void,
|
||||||
|
options: Partial<ToolbarAction> = {}
|
||||||
|
): ToolbarAction => ({
|
||||||
|
key,
|
||||||
|
label,
|
||||||
|
// icon: ICONS[iconType],
|
||||||
|
onClick,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用列配置工厂
|
||||||
|
*/
|
||||||
|
export const createCommonColumns = <T extends Record<string, any>>(): Partial<
|
||||||
|
ProColumns<T>
|
||||||
|
>[] => [
|
||||||
|
{
|
||||||
|
title: "创建时间",
|
||||||
|
dataIndex: "created_at",
|
||||||
|
valueType: "dateTime",
|
||||||
|
sorter: true,
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "更新时间",
|
||||||
|
dataIndex: "updated_at",
|
||||||
|
valueType: "dateTime",
|
||||||
|
hideInSearch: true,
|
||||||
|
hideInTable: true,
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "状态",
|
||||||
|
dataIndex: "status",
|
||||||
|
valueType: "select",
|
||||||
|
valueEnum: {
|
||||||
|
active: { text: "启用", status: "Success" },
|
||||||
|
inactive: { text: "禁用", status: "Error" },
|
||||||
|
pending: { text: "待审核", status: "Processing" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用操作配置工厂
|
||||||
|
*/
|
||||||
|
export const createCommonActions = <T extends { id: number }>(handlers: {
|
||||||
|
onEdit?: (record: T) => void;
|
||||||
|
onDelete?: (record: T) => void;
|
||||||
|
onView?: (record: T) => void;
|
||||||
|
onCopy?: (record: T) => void;
|
||||||
|
}): TableAction<T>[] => {
|
||||||
|
const actions: TableAction<T>[] = [];
|
||||||
|
|
||||||
|
if (handlers.onView) {
|
||||||
|
actions.push(createAction("view", "查看", "view", handlers.onView));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlers.onEdit) {
|
||||||
|
actions.push(
|
||||||
|
createAction("edit", "编辑", "edit", handlers.onEdit, {
|
||||||
|
permission: "edit",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlers.onCopy) {
|
||||||
|
actions.push(
|
||||||
|
createAction("copy", "复制", "copy", handlers.onCopy, {
|
||||||
|
permission: "copy",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlers.onDelete) {
|
||||||
|
actions.push(
|
||||||
|
createAction("delete", "删除", "delete", handlers.onDelete, {
|
||||||
|
danger: true,
|
||||||
|
permission: "delete",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用工具栏操作工厂
|
||||||
|
*/
|
||||||
|
export const createCommonToolbarActions = (handlers: {
|
||||||
|
onAdd?: () => void;
|
||||||
|
onExport?: (selectedRows: any[]) => void;
|
||||||
|
onBatchDelete?: (selectedKeys: React.Key[]) => void;
|
||||||
|
onImport?: () => void;
|
||||||
|
}): ToolbarAction[] => {
|
||||||
|
const actions: ToolbarAction[] = [];
|
||||||
|
|
||||||
|
if (handlers.onAdd) {
|
||||||
|
actions.push(
|
||||||
|
createToolbarAction("add", "新建", "add", handlers.onAdd, {
|
||||||
|
type: "primary",
|
||||||
|
permission: "add",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlers.onImport) {
|
||||||
|
actions.push(
|
||||||
|
createToolbarAction("import", "导入", "upload", handlers.onImport, {
|
||||||
|
permission: "import",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlers.onExport) {
|
||||||
|
actions.push(
|
||||||
|
createToolbarAction(
|
||||||
|
"export",
|
||||||
|
"导出",
|
||||||
|
"export",
|
||||||
|
(_, selectedRows) => handlers.onExport!(selectedRows),
|
||||||
|
{
|
||||||
|
needSelection: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlers.onBatchDelete) {
|
||||||
|
actions.push(
|
||||||
|
createToolbarAction(
|
||||||
|
"batchDelete",
|
||||||
|
"批量删除",
|
||||||
|
"delete",
|
||||||
|
(selectedKeys) => handlers.onBatchDelete!(selectedKeys),
|
||||||
|
{
|
||||||
|
danger: true,
|
||||||
|
needSelection: true,
|
||||||
|
permission: "delete",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户表格配置
|
||||||
|
*/
|
||||||
|
export const createUserTableConfig = () => {
|
||||||
|
const columns: ProColumns<any>[] = [
|
||||||
|
{
|
||||||
|
title: "头像",
|
||||||
|
dataIndex: "avatar",
|
||||||
|
valueType: "avatar",
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 64,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "用户名",
|
||||||
|
dataIndex: "username",
|
||||||
|
copyable: true,
|
||||||
|
ellipsis: true,
|
||||||
|
formItemProps: {
|
||||||
|
rules: [
|
||||||
|
{ required: true, message: "请输入用户名" },
|
||||||
|
{ min: 2, max: 20, message: "用户名长度在 2 到 20 个字符" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "邮箱",
|
||||||
|
dataIndex: "email",
|
||||||
|
copyable: true,
|
||||||
|
formItemProps: {
|
||||||
|
rules: [
|
||||||
|
{ required: true, message: "请输入邮箱" },
|
||||||
|
{ type: "email", message: "请输入正确的邮箱格式" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "手机号",
|
||||||
|
dataIndex: "phone",
|
||||||
|
copyable: true,
|
||||||
|
hideInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "角色",
|
||||||
|
dataIndex: "roles",
|
||||||
|
hideInSearch: true,
|
||||||
|
render: (_, record) => (
|
||||||
|
<>
|
||||||
|
{record.roles?.map((role: string) => (
|
||||||
|
<Tag key={role} color="blue">
|
||||||
|
{role}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
...createCommonColumns(),
|
||||||
|
];
|
||||||
|
|
||||||
|
return { columns };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单表格配置
|
||||||
|
*/
|
||||||
|
export const createOrderTableConfig = () => {
|
||||||
|
const columns: ProColumns<any>[] = [
|
||||||
|
{
|
||||||
|
title: "订单号",
|
||||||
|
dataIndex: "order_no",
|
||||||
|
copyable: true,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "客户名称",
|
||||||
|
dataIndex: "customer_name",
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "订单金额",
|
||||||
|
dataIndex: "amount",
|
||||||
|
valueType: "money",
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "订单状态",
|
||||||
|
dataIndex: "status",
|
||||||
|
valueType: "select",
|
||||||
|
valueEnum: {
|
||||||
|
pending: { text: "待付款", status: "Warning" },
|
||||||
|
paid: { text: "已付款", status: "Processing" },
|
||||||
|
shipped: { text: "已发货", status: "Success" },
|
||||||
|
completed: { text: "已完成", status: "Success" },
|
||||||
|
cancelled: { text: "已取消", status: "Error" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...createCommonColumns(),
|
||||||
|
];
|
||||||
|
|
||||||
|
return { columns };
|
||||||
|
};
|
||||||
58
src/utils/antd/tableHelpers.ts
Normal file
58
src/utils/antd/tableHelpers.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// utils/tableHelpers.ts
|
||||||
|
import { TableDropdownMenuItem } from "@/components/EnhancedProTable/types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建 TableDropdown 菜单项
|
||||||
|
*/
|
||||||
|
export const buildTableDropdownMenuItems = (
|
||||||
|
actions: Array<{
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
danger?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
}>
|
||||||
|
): TableDropdownMenuItem[] => {
|
||||||
|
return actions.map((action) => ({
|
||||||
|
key: action.key,
|
||||||
|
name: action.label,
|
||||||
|
icon: action.icon,
|
||||||
|
danger: action.danger,
|
||||||
|
disabled: action.disabled,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理 TableDropdown 选择事件
|
||||||
|
*/
|
||||||
|
export const handleTableDropdownSelect = <T>(
|
||||||
|
key: string | number,
|
||||||
|
actions: Array<{
|
||||||
|
key: string;
|
||||||
|
onClick: (record: T, action?: any) => void;
|
||||||
|
}>,
|
||||||
|
record: T,
|
||||||
|
action?: any
|
||||||
|
) => {
|
||||||
|
const selectedAction = actions.find((item) => item.key === key);
|
||||||
|
if (selectedAction) {
|
||||||
|
selectedAction.onClick(record, action);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化分页显示文本
|
||||||
|
*/
|
||||||
|
export const formatPaginationTotal = (
|
||||||
|
total: number,
|
||||||
|
range: [number, number]
|
||||||
|
) => {
|
||||||
|
return `第 ${range[0]}-${range[1]} 条/总共 ${total} 条`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成唯一的表格键
|
||||||
|
*/
|
||||||
|
export const generateTableKey = (prefix: string) => {
|
||||||
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user