mirror of
https://gitee.com/JavaLionLi/plus-ui.git
synced 2026-09-13 07:43:43 +08:00
update 重构 封装工具 优化代码
This commit is contained in:
parent
b3e35ea84f
commit
7eccb9b8c1
@ -2,6 +2,7 @@ import type { DataNode, EventDataNode } from 'antd/es/tree';
|
||||
import { LeftOutlined, SearchOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Input, Tree } from 'antd';
|
||||
import { useEffect, useMemo, useState, type Key } from 'react';
|
||||
import { collectTreeKeys } from '@/utils/ruoyi';
|
||||
|
||||
export interface TreePanelProps<T extends object = Record<string, unknown>> {
|
||||
title: string;
|
||||
@ -60,10 +61,6 @@ function toDataNodes<T extends object>(
|
||||
});
|
||||
}
|
||||
|
||||
function collectKeys(nodes: DataNode[]): Key[] {
|
||||
return nodes.flatMap(node => [node.key, ...(node.children?.length ? collectKeys(node.children) : [])]);
|
||||
}
|
||||
|
||||
export default function TreePanel<T extends object = Record<string, unknown>>({
|
||||
title,
|
||||
placeholder = '请输入名称',
|
||||
@ -95,7 +92,7 @@ export default function TreePanel<T extends object = Record<string, unknown>>({
|
||||
() => toDataNodes(filteredData, mergedFieldNames, disabledField),
|
||||
[disabledField, filteredData, mergedFieldNames]
|
||||
);
|
||||
const allTreeKeys = useMemo(() => collectKeys(treeData), [treeData]);
|
||||
const allTreeKeys = useMemo(() => collectTreeKeys(treeData, node => node.key), [treeData]);
|
||||
const mergedCollapsed = collapsed ?? innerCollapsed;
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -6,6 +6,8 @@ import type { UserVO } from '@/api/system/user/types';
|
||||
import type { FlowTaskVO } from '@/api/workflow/task/types';
|
||||
import { currentTaskAllUser, getTask, taskOperation, terminationTask } from '@/api/workflow/task';
|
||||
import UserSelect from '@/components/common/UserSelect';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { confirmTitleSafe } from '@/utils/modal';
|
||||
|
||||
type UserSelectMode = 'transfer' | 'addSignature';
|
||||
|
||||
@ -24,7 +26,7 @@ interface ProcessMeddleProps {
|
||||
}
|
||||
|
||||
export default function ProcessMeddle({ open, taskId, width = 760, onOpenChange, onSuccess }: ProcessMeddleProps) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { loading, withLoading } = useLoading();
|
||||
const [currentTask, setCurrentTask] = useState<FlowTaskVO>();
|
||||
const [userModalOpen, { setTrue: openUserModal, setFalse: closeUserModal }] = useBoolean(false);
|
||||
const [userSelectMode, setUserSelectMode] = useState<UserSelectMode>('transfer');
|
||||
@ -36,19 +38,15 @@ export default function ProcessMeddle({ open, taskId, width = 760, onOpenChange,
|
||||
return;
|
||||
}
|
||||
|
||||
const loadTask = async () => {
|
||||
setLoading(true);
|
||||
setCurrentTask(undefined);
|
||||
try {
|
||||
const loadTask = () =>
|
||||
withLoading(async () => {
|
||||
setCurrentTask(undefined);
|
||||
const res = await getTask(taskId);
|
||||
setCurrentTask(res.data);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
loadTask();
|
||||
}, [open, taskId]);
|
||||
}, [open, taskId, withLoading]);
|
||||
|
||||
const closeWithSuccess = () => {
|
||||
onOpenChange(false);
|
||||
@ -74,18 +72,14 @@ export default function ProcessMeddle({ open, taskId, width = 760, onOpenChange,
|
||||
message.warning('请选择用户');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '是否确认提交?',
|
||||
onOk: async () => {
|
||||
await taskOperation(
|
||||
{ taskId: currentTask.id, userId: user.userId, message: '', messageType: ['1'] },
|
||||
'transferTask'
|
||||
);
|
||||
message.success('操作成功');
|
||||
closeUserModal();
|
||||
closeWithSuccess();
|
||||
}
|
||||
});
|
||||
if (!(await confirmTitleSafe('是否确认提交?'))) return;
|
||||
await taskOperation(
|
||||
{ taskId: currentTask.id, userId: user.userId, message: '', messageType: ['1'] },
|
||||
'transferTask'
|
||||
);
|
||||
message.success('操作成功');
|
||||
closeUserModal();
|
||||
closeWithSuccess();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -94,55 +88,40 @@ export default function ProcessMeddle({ open, taskId, width = 760, onOpenChange,
|
||||
message.warning('请选择用户');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '是否确认提交?',
|
||||
onOk: async () => {
|
||||
await taskOperation({ taskId: currentTask.id, userIds, message: '', messageType: ['1'] }, 'addSignature');
|
||||
message.success('操作成功');
|
||||
closeUserModal();
|
||||
closeWithSuccess();
|
||||
}
|
||||
});
|
||||
if (!(await confirmTitleSafe('是否确认提交?'))) return;
|
||||
await taskOperation({ taskId: currentTask.id, userIds, message: '', messageType: ['1'] }, 'addSignature');
|
||||
message.success('操作成功');
|
||||
closeUserModal();
|
||||
closeWithSuccess();
|
||||
};
|
||||
|
||||
const openReductionSignature = async () => {
|
||||
if (!currentTask?.id) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
await withLoading(async () => {
|
||||
const res = await currentTaskAllUser(currentTask.id);
|
||||
setSignatureUsers((res.data || []).map(item => ({ ...item, nodeName: currentTask.nodeName })));
|
||||
openSignatureModal();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const deleteSignatureUser = async (row: SignatureUser) => {
|
||||
if (!currentTask?.id) return;
|
||||
Modal.confirm({
|
||||
title: '是否确认提交?',
|
||||
onOk: async () => {
|
||||
await taskOperation(
|
||||
{ taskId: currentTask.id, userIds: [row.userId], message: '', messageType: ['1'] },
|
||||
'reductionSignature'
|
||||
);
|
||||
message.success('操作成功');
|
||||
closeSignatureModal();
|
||||
closeWithSuccess();
|
||||
}
|
||||
});
|
||||
if (!(await confirmTitleSafe('是否确认提交?'))) return;
|
||||
await taskOperation(
|
||||
{ taskId: currentTask.id, userIds: [row.userId], message: '', messageType: ['1'] },
|
||||
'reductionSignature'
|
||||
);
|
||||
message.success('操作成功');
|
||||
closeSignatureModal();
|
||||
closeWithSuccess();
|
||||
};
|
||||
|
||||
const submitTermination = async () => {
|
||||
if (!currentTask?.id) return;
|
||||
Modal.confirm({
|
||||
title: '是否确认终止?',
|
||||
onOk: async () => {
|
||||
await terminationTask({ taskId: currentTask.id, comment: '' });
|
||||
message.success('操作成功');
|
||||
closeWithSuccess();
|
||||
}
|
||||
});
|
||||
if (!(await confirmTitleSafe('是否确认终止?'))) return;
|
||||
await terminationTask({ taskId: currentTask.id, comment: '' });
|
||||
message.success('操作成功');
|
||||
closeWithSuccess();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -25,6 +25,8 @@ import {
|
||||
import FileUpload from '@/components/common/FileUpload';
|
||||
import UserSelect from '@/components/common/UserSelect';
|
||||
import MessageType from '@/components/workflow/MessageType';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { confirmTitleSafe } from '@/utils/modal';
|
||||
|
||||
type UserSelectMode = 'copy' | 'assigneeMap' | 'transfer' | 'delegate' | 'addSignature';
|
||||
|
||||
@ -46,16 +48,6 @@ function buttonVisible(task?: FlowTaskVO, code?: string) {
|
||||
return !!task?.buttonList?.find(item => item.code === code && item.show);
|
||||
}
|
||||
|
||||
function confirmAction(title: string) {
|
||||
return new Promise<boolean>(resolve => {
|
||||
Modal.confirm({
|
||||
title,
|
||||
onOk: () => resolve(true),
|
||||
onCancel: () => resolve(false)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default function SubmitVerify({ open, taskId, variables = {}, onOpenChange, onSubmitted }: SubmitVerifyProps) {
|
||||
const [approveForm] = Form.useForm<{
|
||||
message?: string;
|
||||
@ -64,7 +56,7 @@ export default function SubmitVerify({ open, taskId, variables = {}, onOpenChang
|
||||
fileId?: string;
|
||||
}>();
|
||||
const [backForm] = Form.useForm<{ nodeCode?: string; message?: string; messageType?: string[]; fileId?: string }>();
|
||||
const [approvalLoading, setApprovalLoading] = useState(false);
|
||||
const { loading: approvalLoading, setLoading: setApprovalLoading, withLoading: withApprovalLoading } = useLoading();
|
||||
const [currentTask, setCurrentTask] = useState<FlowTaskVO>();
|
||||
const [nextNodes, setNextNodes] = useState<FlowNextNodeVO[]>([]);
|
||||
const [backOpen, { setTrue: openBackModal, setFalse: closeBackModal }] = useBoolean(false);
|
||||
@ -121,7 +113,7 @@ export default function SubmitVerify({ open, taskId, variables = {}, onOpenChang
|
||||
openUserModal();
|
||||
};
|
||||
|
||||
const submitUserSelect = (users: UserVO[]) => {
|
||||
const submitUserSelect = async (users: UserVO[]) => {
|
||||
if (userSelectMode === 'copy') {
|
||||
setCopyUsers(users);
|
||||
closeUserModal();
|
||||
@ -155,25 +147,21 @@ export default function SubmitVerify({ open, taskId, variables = {}, onOpenChang
|
||||
message.warning('请选择用户');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '是否确认提交?',
|
||||
onOk: async () => {
|
||||
const values = approveForm.getFieldsValue();
|
||||
await taskOperation(
|
||||
{
|
||||
taskId: currentTask.id,
|
||||
userId: user.userId,
|
||||
message: values.message,
|
||||
messageType: values.messageType || ['1'],
|
||||
variables
|
||||
},
|
||||
userSelectMode === 'transfer' ? 'transferTask' : 'delegateTask'
|
||||
);
|
||||
message.success('操作成功');
|
||||
closeUserModal();
|
||||
finishWorkflowAction();
|
||||
}
|
||||
});
|
||||
if (!(await confirmTitleSafe('是否确认提交?'))) return;
|
||||
const values = approveForm.getFieldsValue();
|
||||
await taskOperation(
|
||||
{
|
||||
taskId: currentTask.id,
|
||||
userId: user.userId,
|
||||
message: values.message,
|
||||
messageType: values.messageType || ['1'],
|
||||
variables
|
||||
},
|
||||
userSelectMode === 'transfer' ? 'transferTask' : 'delegateTask'
|
||||
);
|
||||
message.success('操作成功');
|
||||
closeUserModal();
|
||||
finishWorkflowAction();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -182,25 +170,21 @@ export default function SubmitVerify({ open, taskId, variables = {}, onOpenChang
|
||||
message.warning('请选择用户');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '是否确认提交?',
|
||||
onOk: async () => {
|
||||
const values = approveForm.getFieldsValue();
|
||||
await taskOperation(
|
||||
{
|
||||
taskId: currentTask.id,
|
||||
userIds,
|
||||
message: values.message,
|
||||
messageType: values.messageType || ['1'],
|
||||
variables
|
||||
},
|
||||
'addSignature'
|
||||
);
|
||||
message.success('操作成功');
|
||||
closeUserModal();
|
||||
finishWorkflowAction();
|
||||
}
|
||||
});
|
||||
if (!(await confirmTitleSafe('是否确认提交?'))) return;
|
||||
const values = approveForm.getFieldsValue();
|
||||
await taskOperation(
|
||||
{
|
||||
taskId: currentTask.id,
|
||||
userIds,
|
||||
message: values.message,
|
||||
messageType: values.messageType || ['1'],
|
||||
variables
|
||||
},
|
||||
'addSignature'
|
||||
);
|
||||
message.success('操作成功');
|
||||
closeUserModal();
|
||||
finishWorkflowAction();
|
||||
};
|
||||
|
||||
const removeCopyUser = (userId?: string | number) => {
|
||||
@ -210,9 +194,8 @@ export default function SubmitVerify({ open, taskId, variables = {}, onOpenChang
|
||||
const completeApproval = async () => {
|
||||
if (!currentTask?.id) return;
|
||||
const values = await approveForm.validateFields();
|
||||
if (!(await confirmAction('是否确认提交?'))) return;
|
||||
setApprovalLoading(true);
|
||||
try {
|
||||
if (!(await confirmTitleSafe('是否确认提交?'))) return;
|
||||
await withApprovalLoading(async () => {
|
||||
await completeTask({
|
||||
taskId: currentTask.id,
|
||||
message: values.message,
|
||||
@ -224,16 +207,13 @@ export default function SubmitVerify({ open, taskId, variables = {}, onOpenChang
|
||||
});
|
||||
message.success('操作成功');
|
||||
finishWorkflowAction();
|
||||
} finally {
|
||||
setApprovalLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const openBack = async () => {
|
||||
if (!currentTask?.id) return;
|
||||
openBackModal();
|
||||
setApprovalLoading(true);
|
||||
try {
|
||||
await withApprovalLoading(async () => {
|
||||
const res = await getBackTaskNode(currentTask.id, currentTask.nodeCode);
|
||||
setBackNodes(res.data || []);
|
||||
backForm.setFieldsValue({
|
||||
@ -242,17 +222,14 @@ export default function SubmitVerify({ open, taskId, variables = {}, onOpenChang
|
||||
messageType: ['1'],
|
||||
fileId: undefined
|
||||
});
|
||||
} finally {
|
||||
setApprovalLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const submitBack = async () => {
|
||||
if (!currentTask?.id) return;
|
||||
const values = await backForm.validateFields();
|
||||
if (!(await confirmAction('是否确认驳回?'))) return;
|
||||
setApprovalLoading(true);
|
||||
try {
|
||||
if (!(await confirmTitleSafe('是否确认驳回?'))) return;
|
||||
await withApprovalLoading(async () => {
|
||||
await backProcess({
|
||||
...values,
|
||||
taskId: currentTask.id,
|
||||
@ -261,58 +238,46 @@ export default function SubmitVerify({ open, taskId, variables = {}, onOpenChang
|
||||
message.success('操作成功');
|
||||
closeBackModal();
|
||||
finishWorkflowAction();
|
||||
} finally {
|
||||
setApprovalLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const submitTermination = async () => {
|
||||
if (!currentTask?.id) return;
|
||||
const values = approveForm.getFieldsValue();
|
||||
if (!(await confirmAction('是否确认终止?'))) return;
|
||||
setApprovalLoading(true);
|
||||
try {
|
||||
if (!(await confirmTitleSafe('是否确认终止?'))) return;
|
||||
await withApprovalLoading(async () => {
|
||||
await terminationTask({ taskId: currentTask.id, comment: values.message });
|
||||
message.success('操作成功');
|
||||
finishWorkflowAction();
|
||||
} finally {
|
||||
setApprovalLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const openReductionSignature = async () => {
|
||||
if (!currentTask?.id) return;
|
||||
setApprovalLoading(true);
|
||||
try {
|
||||
await withApprovalLoading(async () => {
|
||||
const res = await currentTaskAllUser(currentTask.id);
|
||||
setSignatureUsers((res.data || []).map(item => ({ ...item, nodeName: currentTask.nodeName })));
|
||||
openSignatureModal();
|
||||
} finally {
|
||||
setApprovalLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const deleteSignatureUser = async (row: SignatureUser) => {
|
||||
if (!currentTask?.id) return;
|
||||
const values = approveForm.getFieldsValue();
|
||||
Modal.confirm({
|
||||
title: '是否确认提交?',
|
||||
onOk: async () => {
|
||||
await taskOperation(
|
||||
{
|
||||
taskId: currentTask.id,
|
||||
userIds: [row.userId],
|
||||
message: values.message,
|
||||
messageType: values.messageType || ['1'],
|
||||
variables
|
||||
},
|
||||
'reductionSignature'
|
||||
);
|
||||
message.success('操作成功');
|
||||
closeSignatureModal();
|
||||
finishWorkflowAction();
|
||||
}
|
||||
});
|
||||
if (!(await confirmTitleSafe('是否确认提交?'))) return;
|
||||
await taskOperation(
|
||||
{
|
||||
taskId: currentTask.id,
|
||||
userIds: [row.userId],
|
||||
message: values.message,
|
||||
messageType: values.messageType || ['1'],
|
||||
variables
|
||||
},
|
||||
'reductionSignature'
|
||||
);
|
||||
message.success('操作成功');
|
||||
closeSignatureModal();
|
||||
finishWorkflowAction();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
25
src/hooks/useDateRangeQuery.ts
Normal file
25
src/hooks/useDateRangeQuery.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import type { ConfigType } from 'dayjs';
|
||||
import { addDateRange, formatDateTimeRange } from '@/utils/ruoyi';
|
||||
|
||||
export type DateRangeValue = [ConfigType, ConfigType] | null | undefined;
|
||||
|
||||
export function useDateRangeQuery(propName?: string) {
|
||||
const [dateRange, setDateRange] = useState<DateRangeValue>();
|
||||
|
||||
const applyDateRange = useCallback(
|
||||
<T extends Record<string, unknown>>(queryParams: T, range: DateRangeValue = dateRange) => {
|
||||
return addDateRange(queryParams, formatDateTimeRange(range), propName);
|
||||
},
|
||||
[dateRange, propName]
|
||||
);
|
||||
|
||||
const resetDateRange = useCallback(() => setDateRange(undefined), []);
|
||||
|
||||
return {
|
||||
dateRange,
|
||||
setDateRange,
|
||||
applyDateRange,
|
||||
resetDateRange
|
||||
};
|
||||
}
|
||||
20
src/hooks/useLoading.ts
Normal file
20
src/hooks/useLoading.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
export function useLoading(initialValue = false) {
|
||||
const [loading, setLoading] = useState(initialValue);
|
||||
|
||||
const withLoading = useCallback(async <T>(task: () => Promise<T>) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
return await task();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
loading,
|
||||
setLoading,
|
||||
withLoading
|
||||
};
|
||||
}
|
||||
9
src/hooks/useSearchReset.ts
Normal file
9
src/hooks/useSearchReset.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { useCallback, type RefObject } from 'react';
|
||||
import type { ActionType } from '@ant-design/pro-components';
|
||||
|
||||
export function useSearchReset(actionRef: RefObject<ActionType | undefined>, resetExtras?: () => void) {
|
||||
return useCallback(() => {
|
||||
resetExtras?.();
|
||||
setTimeout(() => actionRef.current?.reloadAndRest?.(), 0);
|
||||
}, [actionRef, resetExtras]);
|
||||
}
|
||||
54
src/hooks/useTreeTableExpand.ts
Normal file
54
src/hooks/useTreeTableExpand.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import type { Key } from 'react';
|
||||
import { collectTreeKeys } from '@/utils/ruoyi';
|
||||
|
||||
interface UseTreeTableExpandOptions<T extends object> {
|
||||
initialExpandAll?: boolean;
|
||||
getChildren?: (row: T) => T[] | undefined;
|
||||
}
|
||||
|
||||
export function useTreeTableExpand<T extends object>(
|
||||
getKey: (row: T) => Key,
|
||||
options: UseTreeTableExpandOptions<T> = {}
|
||||
) {
|
||||
const { initialExpandAll = false, getChildren } = options;
|
||||
const [expandAll, setExpandAll] = useState(initialExpandAll);
|
||||
const [expandedRowKeys, setExpandedRowKeys] = useState<Key[]>([]);
|
||||
|
||||
const getAllKeys = useCallback(
|
||||
(rows: T[]) => collectTreeKeys(rows, getKey, getChildren),
|
||||
[getChildren, getKey]
|
||||
);
|
||||
|
||||
const syncExpandedRows = useCallback(
|
||||
(rows: T[], expanded = expandAll) => {
|
||||
setExpandedRowKeys(expanded ? getAllKeys(rows) : []);
|
||||
},
|
||||
[expandAll, getAllKeys]
|
||||
);
|
||||
|
||||
const toggleExpandAll = useCallback(
|
||||
(rows: T[]) => {
|
||||
setExpandAll(current => {
|
||||
const next = !current;
|
||||
setExpandedRowKeys(next ? getAllKeys(rows) : []);
|
||||
return next;
|
||||
});
|
||||
},
|
||||
[getAllKeys]
|
||||
);
|
||||
|
||||
const onExpandedRowsChange = useCallback((keys: readonly Key[]) => {
|
||||
setExpandedRowKeys([...keys]);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
expandAll,
|
||||
expandedRowKeys,
|
||||
setExpandAll,
|
||||
setExpandedRowKeys,
|
||||
syncExpandedRows,
|
||||
toggleExpandAll,
|
||||
onExpandedRowsChange
|
||||
};
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
import type { Key } from 'react';
|
||||
import { DeleteOutlined, EditOutlined, PlusOutlined, SortAscendingOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
ModalForm,
|
||||
@ -15,6 +14,7 @@ import { useMemo, useRef, useState } from 'react';
|
||||
import type { TreeForm, TreeQuery, TreeVO } from '@/api/demo/tree/types';
|
||||
import { addTree, delTree, getTree, listTree, updateTree } from '@/api/demo/tree';
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
import { useTreeTableExpand } from '@/hooks/useTreeTableExpand';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { handleTree } from '@/utils/ruoyi';
|
||||
@ -35,18 +35,14 @@ function toTreeSelectData(nodes: TreeVO[]): TreeSelectNode[] {
|
||||
}));
|
||||
}
|
||||
|
||||
function collectTreeKeys(nodes: TreeVO[]): Key[] {
|
||||
return nodes.flatMap(node => [node.id, ...(node.children ? collectTreeKeys(node.children) : [])]);
|
||||
}
|
||||
|
||||
export default function DemoTreePage() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
const [form] = Form.useForm<TreeForm>();
|
||||
const userInfo = useUserStore(state => state.userInfo);
|
||||
const [treeOptions, setTreeOptions] = useState<TreeVO[]>([]);
|
||||
const [tableRows, setTableRows] = useState<TreeVO[]>([]);
|
||||
const [expandAll, setExpandAll] = useState(false);
|
||||
const [expandedRowKeys, setExpandedRowKeys] = useState<Key[]>([]);
|
||||
const { expandAll, expandedRowKeys, onExpandedRowsChange, syncExpandedRows, toggleExpandAll } =
|
||||
useTreeTableExpand<TreeVO>(row => row.id);
|
||||
const [modalOpen, { setTrue: openModal, setFalse: closeModal }] = useBoolean(false);
|
||||
const [modalTitle, setModalTitle] = useState('');
|
||||
|
||||
@ -134,13 +130,13 @@ export default function DemoTreePage() {
|
||||
search={{ labelWidth: 90 }}
|
||||
expandable={{
|
||||
expandedRowKeys,
|
||||
onExpandedRowsChange: keys => setExpandedRowKeys([...keys])
|
||||
onExpandedRowsChange
|
||||
}}
|
||||
request={async params => {
|
||||
const res = await listTree({ treeName: params.treeName });
|
||||
const rows = handleTree<TreeVO>(res.data || [], 'id', 'parentId');
|
||||
setTableRows(rows);
|
||||
setExpandedRowKeys(expandAll ? collectTreeKeys(rows) : []);
|
||||
syncExpandedRows(rows, expandAll);
|
||||
return { data: rows, total: rows.length, success: true };
|
||||
}}
|
||||
toolbar={{ title: '测试树列表' }}
|
||||
@ -153,13 +149,7 @@ export default function DemoTreePage() {
|
||||
<Button
|
||||
key="expand"
|
||||
icon={<SortAscendingOutlined />}
|
||||
onClick={() => {
|
||||
setExpandAll(value => {
|
||||
const next = !value;
|
||||
setExpandedRowKeys(next ? collectTreeKeys(tableRows) : []);
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
onClick={() => toggleExpandAll(tableRows)}
|
||||
>
|
||||
展开/折叠
|
||||
</Button>
|
||||
|
||||
12
src/pages/monitor/cache/index.tsx
vendored
12
src/pages/monitor/cache/index.tsx
vendored
@ -6,6 +6,7 @@ import ReactECharts from 'echarts-for-react';
|
||||
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import type { CacheVO } from '@/api/monitor/cache/types';
|
||||
import { getCache } from '@/api/monitor/cache';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
|
||||
function infoValue(cache: Partial<CacheVO>, key: string) {
|
||||
return cache.info?.[key] || '-';
|
||||
@ -44,17 +45,14 @@ function overviewRows(cache: Partial<CacheVO>) {
|
||||
|
||||
export default function MonitorCachePage() {
|
||||
const [cache, setCache] = useState<Partial<CacheVO>>({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { loading, withLoading } = useLoading();
|
||||
|
||||
const loadOverview = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await withLoading(async () => {
|
||||
const cacheRes = await getCache();
|
||||
setCache(cacheRes.data || {});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
});
|
||||
}, [withLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
loadOverview();
|
||||
|
||||
@ -3,20 +3,18 @@ import { PageContainer, ProTable, type ActionType, type ProColumns } from '@ant-
|
||||
import { Button, message, Popconfirm } from 'antd';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import type { LoginInfoQuery, LoginInfoVO } from '@/api/monitor/logininfo/types';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import { cleanLoginInfo, delLoginInfo, listLoginInfo, unlockLoginInfo } from '@/api/monitor/logininfo';
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
import EllipsisText from '@/components/common/EllipsisText';
|
||||
import { useDateRangeQuery } from '@/hooks/useDateRangeQuery';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { addDateRange, formatDateTimeRange, toPageQuery, toTableData, withTableSort } from '@/utils/ruoyi';
|
||||
import { toPageQuery, toTableData, withTableSort } from '@/utils/ruoyi';
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
export default function MonitorLoginInfoPage() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
@ -26,6 +24,7 @@ export default function MonitorLoginInfoPage() {
|
||||
row => row.infoId
|
||||
);
|
||||
const { updateExportParams, exportFile } = useTableExport();
|
||||
const { applyDateRange: applyLoginTimeDateRange } = useDateRangeQuery();
|
||||
|
||||
const canRemove = hasPermi(userInfo, ['monitor:logininfo:remove']);
|
||||
const canUnlock = hasPermi(userInfo, ['monitor:logininfo:unlock']);
|
||||
@ -136,9 +135,9 @@ export default function MonitorLoginInfoPage() {
|
||||
rowSelection={{ selectedRowKeys: ids, onChange: handleSelectionChange }}
|
||||
request={async (params, sort) => {
|
||||
const { loginTimeRange, ...tableParams } = params;
|
||||
const query = addDateRange(
|
||||
const query = applyLoginTimeDateRange(
|
||||
withTableSort(toPageQuery(tableParams), sort, { orderByColumn: 'loginTime', isAsc: 'descending' }),
|
||||
formatDateTimeRange(loginTimeRange)
|
||||
loginTimeRange
|
||||
);
|
||||
updateExportParams(query);
|
||||
const res = await listLoginInfo(query);
|
||||
|
||||
@ -3,19 +3,16 @@ import { PageContainer, ProTable, type ActionType, type ProColumns } from '@ant-
|
||||
import { message } from 'antd';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import type { OnlineQuery, OnlineVO } from '@/api/monitor/online/types';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import { forceLogout, listOnline } from '@/api/monitor/online';
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
import EllipsisText from '@/components/common/EllipsisText';
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
export default function MonitorOnlinePage() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
|
||||
@ -10,16 +10,15 @@ import DictTag from '@/components/common/DictTag';
|
||||
import EllipsisText from '@/components/common/EllipsisText';
|
||||
import JsonViewer from '@/components/common/JsonViewer';
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
import { useDateRangeQuery } from '@/hooks/useDateRangeQuery';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { addDateRange, formatDateTimeRange, toPageQuery, toTableData, withTableSort } from '@/utils/ruoyi';
|
||||
import { toPageQuery, toTableData, withTableSort } from '@/utils/ruoyi';
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function dictText(dicts: DictData[] | undefined, value?: string | number) {
|
||||
const normalized = value === undefined || value === null ? undefined : String(value);
|
||||
@ -34,6 +33,7 @@ export default function MonitorOperlogPage() {
|
||||
const [detailOpen, { setTrue: openDetailModal, setFalse: closeDetailModal }] = useBoolean(false);
|
||||
const [detail, setDetail] = useState<OperLogVO>();
|
||||
const { updateExportParams, exportFile } = useTableExport();
|
||||
const { applyDateRange: applyOperTimeDateRange } = useDateRangeQuery();
|
||||
|
||||
const canQuery = hasPermi(userInfo, ['monitor:operlog:query']);
|
||||
const canRemove = hasPermi(userInfo, ['monitor:operlog:remove']);
|
||||
@ -167,9 +167,9 @@ export default function MonitorOperlogPage() {
|
||||
rowSelection={{ selectedRowKeys: ids, onChange: handleSelectionChange }}
|
||||
request={async (params, sort) => {
|
||||
const { operTimeRange, ...tableParams } = params;
|
||||
const query = addDateRange(
|
||||
const query = applyOperTimeDateRange(
|
||||
withTableSort(toPageQuery(tableParams), sort, { orderByColumn: 'operTime', isAsc: 'descending' }),
|
||||
formatDateTimeRange(operTimeRange)
|
||||
operTimeRange
|
||||
);
|
||||
updateExportParams(query);
|
||||
const res = await listOperlog(query);
|
||||
|
||||
@ -8,6 +8,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import type { RegisterParams, VerifyCodeResult } from '@/api/types';
|
||||
import { getCodeImg, register } from '@/api/login';
|
||||
import LocaleSelect from '@/components/layout/LocaleSelect';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { useAppStore } from '@/stores/appStore';
|
||||
import { appEnv } from '@/utils/env';
|
||||
|
||||
@ -70,7 +71,7 @@ export default function Register() {
|
||||
const appLocale = useAppStore(state => state.appLocale);
|
||||
const setAppLocale = useAppStore(state => state.setAppLocale);
|
||||
const [captcha, setCaptcha] = useState<VerifyCodeResult>({ captchaEnabled: true });
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { loading, withLoading } = useLoading();
|
||||
const text = registerText[appLocale];
|
||||
|
||||
const loadCaptcha = useCallback(async () => {
|
||||
@ -90,26 +91,25 @@ export default function Register() {
|
||||
}, []);
|
||||
|
||||
const submitRegister = async (values: RegisterParams) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await register({
|
||||
...values,
|
||||
uuid: captcha.uuid,
|
||||
userType: 'sys_user'
|
||||
});
|
||||
await Modal.success({
|
||||
title: text.successTitle,
|
||||
content: text.success(values.username)
|
||||
});
|
||||
history.push('/login');
|
||||
} catch (error) {
|
||||
if (captcha.captchaEnabled) {
|
||||
loadCaptcha();
|
||||
await withLoading(async () => {
|
||||
try {
|
||||
await register({
|
||||
...values,
|
||||
uuid: captcha.uuid,
|
||||
userType: 'sys_user'
|
||||
});
|
||||
await Modal.success({
|
||||
title: text.successTitle,
|
||||
content: text.success(values.username)
|
||||
});
|
||||
history.push('/login');
|
||||
} catch (error) {
|
||||
if (captcha.captchaEnabled) {
|
||||
loadCaptcha();
|
||||
}
|
||||
message.error((error as Error).message || text.fail);
|
||||
}
|
||||
message.error((error as Error).message || text.fail);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -12,10 +12,9 @@ import {
|
||||
type ProColumns
|
||||
} from '@ant-design/pro-components';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message, Modal, Popconfirm, Space, Switch } from 'antd';
|
||||
import { Button, Form, message, Popconfirm, Space, Switch } from 'antd';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import type { ClientForm, ClientQuery, ClientVO } from '@/api/system/client/types';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import { addClient, changeStatus, delClient, getClient, listClient, updateClient } from '@/api/system/client';
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
import EllipsisText from '@/components/common/EllipsisText';
|
||||
@ -24,14 +23,13 @@ import { useDict } from '@/hooks/useDict';
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { confirmAction } from '@/utils/modal';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
const defaultClientForm: ClientForm = { status: '0' };
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function getRuleList(ruleList?: string[], ruleValue?: string) {
|
||||
if (Array.isArray(ruleList) && ruleList.length) return ruleList;
|
||||
@ -42,17 +40,6 @@ function getRuleList(ruleList?: string[], ruleValue?: string) {
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function confirmAction(content: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
Modal.confirm({
|
||||
title: '系统提示',
|
||||
content,
|
||||
onOk: () => resolve(),
|
||||
onCancel: () => reject(new Error('cancelled'))
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default function SystemClientPage() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
const [form] = Form.useForm<ClientForm>();
|
||||
|
||||
@ -13,21 +13,19 @@ import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message, Popconfirm } from 'antd';
|
||||
import { useRef, useState } from 'react';
|
||||
import type { ConfigForm, ConfigQuery, ConfigVO } from '@/api/system/config/types';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import { addConfig, delConfig, getConfig, listConfig, refreshConfigCache, updateConfig } from '@/api/system/config';
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
import EllipsisText from '@/components/common/EllipsisText';
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
import { useDateRangeQuery } from '@/hooks/useDateRangeQuery';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { addDateRange, formatDateTimeRange, toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
export default function SystemConfigPage() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
@ -38,6 +36,7 @@ export default function SystemConfigPage() {
|
||||
const [modalOpen, { setTrue: openModal, setFalse: closeModal }] = useBoolean(false);
|
||||
const [modalTitle, setModalTitle] = useState('');
|
||||
const { updateExportParams, exportFile } = useTableExport();
|
||||
const { applyDateRange: applyCreateTimeDateRange } = useDateRangeQuery();
|
||||
const canAdd = hasPermi(userInfo, ['system:config:add']);
|
||||
const canEdit = hasPermi(userInfo, ['system:config:edit']);
|
||||
const canRemove = hasPermi(userInfo, ['system:config:remove']);
|
||||
@ -132,7 +131,7 @@ export default function SystemConfigPage() {
|
||||
rowSelection={{ selectedRowKeys: ids, onChange: handleSelectionChange }}
|
||||
request={async params => {
|
||||
const { createTimeRange, ...tableParams } = params;
|
||||
const query = addDateRange(toPageQuery(tableParams), formatDateTimeRange(createTimeRange));
|
||||
const query = applyCreateTimeDateRange(toPageQuery(tableParams), createTimeRange);
|
||||
updateExportParams(query);
|
||||
const res = await listConfig(query);
|
||||
return toTableData(res);
|
||||
|
||||
@ -13,16 +13,17 @@ import {
|
||||
} from '@ant-design/pro-components';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message, Tag } from 'antd';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import type { DeptForm, DeptQuery, DeptVO } from '@/api/system/dept/types';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import type { UserVO } from '@/api/system/user/types';
|
||||
import { addDept, delDept, getDept, listDept, listDeptExcludeChild, updateDept } from '@/api/system/dept';
|
||||
import { listUserByDeptId } from '@/api/system/user';
|
||||
import EllipsisText from '@/components/common/EllipsisText';
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useTreeTableExpand } from '@/hooks/useTreeTableExpand';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { handleTree } from '@/utils/ruoyi';
|
||||
|
||||
@ -37,12 +38,6 @@ interface TreeSelectNode {
|
||||
children?: TreeSelectNode[];
|
||||
}
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({
|
||||
label: item.dictLabel,
|
||||
value: item.dictValue
|
||||
}));
|
||||
}
|
||||
|
||||
function toTreeSelectData(depts: DeptVO[]): TreeSelectNode[] {
|
||||
return depts.map(dept => ({
|
||||
@ -52,16 +47,6 @@ function toTreeSelectData(depts: DeptVO[]): TreeSelectNode[] {
|
||||
}));
|
||||
}
|
||||
|
||||
function collectDeptKeys(depts: DeptVO[]): Array<string | number> {
|
||||
return depts.reduce<Array<string | number>>((keys, dept) => {
|
||||
keys.push(dept.deptId);
|
||||
if (dept.children?.length) {
|
||||
keys.push(...collectDeptKeys(dept.children));
|
||||
}
|
||||
return keys;
|
||||
}, []);
|
||||
}
|
||||
|
||||
export default function SystemDeptPage() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
const [form] = Form.useForm<DeptForm>();
|
||||
@ -71,8 +56,11 @@ export default function SystemDeptPage() {
|
||||
const [modalTitle, setModalTitle] = useState('');
|
||||
const [deptOptions, setDeptOptions] = useState<DeptVO[]>([]);
|
||||
const [deptUserList, setDeptUserList] = useState<UserVO[]>([]);
|
||||
const [expandedRowKeys, setExpandedRowKeys] = useState<readonly React.Key[]>([]);
|
||||
const [lastDeptList, setLastDeptList] = useState<DeptVO[]>([]);
|
||||
const { expandedRowKeys, onExpandedRowsChange, syncExpandedRows, toggleExpandAll } = useTreeTableExpand<DeptVO>(
|
||||
dept => dept.deptId,
|
||||
{ initialExpandAll: true }
|
||||
);
|
||||
|
||||
const canAdd = hasPermi(userInfo, ['system:dept:add']);
|
||||
const canEdit = hasPermi(userInfo, ['system:dept:edit']);
|
||||
@ -80,10 +68,6 @@ export default function SystemDeptPage() {
|
||||
const parentId = Form.useWatch('parentId', form);
|
||||
const deptTreeSelectData = useMemo(() => toTreeSelectData(deptOptions), [deptOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
setExpandedRowKeys(collectDeptKeys(lastDeptList));
|
||||
}, [lastDeptList]);
|
||||
|
||||
const loadDeptOptions = async (excludeDeptId?: string | number) => {
|
||||
const res = excludeDeptId ? await listDeptExcludeChild(excludeDeptId) : await listDept();
|
||||
const tree = handleTree<DeptVO>(res.data || [], 'deptId');
|
||||
@ -212,12 +196,13 @@ export default function SystemDeptPage() {
|
||||
pagination={false}
|
||||
expandable={{
|
||||
expandedRowKeys,
|
||||
onExpandedRowsChange: setExpandedRowKeys
|
||||
onExpandedRowsChange
|
||||
}}
|
||||
request={async params => {
|
||||
const res = await listDept(params);
|
||||
const data = handleTree<DeptVO>(res.data || [], 'deptId');
|
||||
setLastDeptList(data);
|
||||
syncExpandedRows(data);
|
||||
return { data, success: true };
|
||||
}}
|
||||
toolbar={{ title: '部门列表' }}
|
||||
@ -229,7 +214,7 @@ export default function SystemDeptPage() {
|
||||
),
|
||||
<Button
|
||||
key="expand"
|
||||
onClick={() => setExpandedRowKeys(expandedRowKeys.length ? [] : collectDeptKeys(lastDeptList))}
|
||||
onClick={() => toggleExpandAll(lastDeptList)}
|
||||
>
|
||||
展开/折叠
|
||||
</Button>
|
||||
|
||||
@ -14,7 +14,6 @@ import {
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message, Modal, Space, Tag, Tree } from 'antd';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import type { MenuForm, MenuQuery, MenuTreeOption, MenuVO } from '@/api/system/menu/types';
|
||||
import { addMenu, cascadeDelMenu, delMenu, getMenu, listMenu, treeselect, updateMenu } from '@/api/system/menu';
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
@ -22,8 +21,10 @@ import EllipsisText from '@/components/common/EllipsisText';
|
||||
import IconSelect from '@/components/common/IconSelect';
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { routeIcon } from '@/utils/menu';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { handleTree } from '@/utils/ruoyi';
|
||||
|
||||
@ -37,9 +38,6 @@ const defaultMenuForm: MenuForm = {
|
||||
status: '0'
|
||||
};
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function menuTypeMeta(row: MenuVO) {
|
||||
if (row.menuType === 'F') return { label: '按钮', color: 'orange' };
|
||||
@ -66,7 +64,7 @@ export default function SystemMenuPage() {
|
||||
const [modalTitle, setModalTitle] = useState('');
|
||||
const [cascadeOpen, { setTrue: openCascadeModal, setFalse: closeCascadeModal }] = useBoolean(false);
|
||||
const [cascadeKeys, setCascadeKeys] = useState<Array<string | number>>([]);
|
||||
const [cascadeLoading, setCascadeLoading] = useState(false);
|
||||
const { loading: cascadeLoading, withLoading: withCascadeLoading } = useLoading();
|
||||
|
||||
const canAdd = hasPermi(userInfo, ['system:menu:add']);
|
||||
const canEdit = hasPermi(userInfo, ['system:menu:edit']);
|
||||
@ -122,15 +120,12 @@ export default function SystemMenuPage() {
|
||||
message.warning('请选择要删除的菜单');
|
||||
return;
|
||||
}
|
||||
setCascadeLoading(true);
|
||||
try {
|
||||
await withCascadeLoading(async () => {
|
||||
await cascadeDelMenu(cascadeKeys);
|
||||
message.success('删除成功');
|
||||
closeCascadeModal();
|
||||
actionRef.current?.reload();
|
||||
} finally {
|
||||
setCascadeLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const columns: ProColumns<MenuVO>[] = [
|
||||
|
||||
@ -14,7 +14,6 @@ import { history, useLocation } from '@umijs/max';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Descriptions, Form, message, Modal, Popconfirm } from 'antd';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import type { NoticeForm, NoticeQuery, NoticeVO } from '@/api/system/notice/types';
|
||||
import { addNotice, delNotice, getNotice, listNotice, updateNotice } from '@/api/system/notice';
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
@ -25,6 +24,7 @@ import { useDict } from '@/hooks/useDict';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { resolveOssContent } from '@/utils/ossContent';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
import { sanitizeHtml } from '@/utils/sanitize';
|
||||
@ -32,9 +32,6 @@ import { sanitizeHtml } from '@/utils/sanitize';
|
||||
const defaultNoticeForm: NoticeForm = { status: '0' };
|
||||
const emptyNoticeContent = '<p>暂无公告内容</p>';
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function SafeHtmlContent({ html }: { html: string }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -10,9 +10,8 @@ import {
|
||||
type ProColumns
|
||||
} from '@ant-design/pro-components';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message, Modal, Popconfirm, Switch, Tag } from 'antd';
|
||||
import { Button, Form, message, Popconfirm, Switch, Tag } from 'antd';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import type { OssConfigForm, OssConfigQuery, OssConfigVO } from '@/api/system/ossConfig/types';
|
||||
import {
|
||||
addOssConfig,
|
||||
@ -27,6 +26,8 @@ import RowActions from '@/components/common/RowActions';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { confirmAction } from '@/utils/modal';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
@ -36,21 +37,6 @@ const defaultOssConfigForm: OssConfigForm = {
|
||||
status: 'N'
|
||||
};
|
||||
|
||||
function confirmAction(content: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
Modal.confirm({
|
||||
title: '系统提示',
|
||||
content,
|
||||
onOk: () => resolve(),
|
||||
onCancel: () => reject(new Error('cancelled'))
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function accessPolicyTag(value?: string) {
|
||||
if (value === '0') return <Tag color="orange">private</Tag>;
|
||||
if (value === '1') return <Tag color="green">public</Tag>;
|
||||
|
||||
@ -19,10 +19,11 @@ import FileUpload from '@/components/common/FileUpload';
|
||||
import ImagePreview from '@/components/common/ImagePreview';
|
||||
import ImageUpload from '@/components/common/ImageUpload';
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
import { useDateRangeQuery } from '@/hooks/useDateRangeQuery';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { saveValidatedBlob } from '@/utils/download';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { addDateRange, formatDateTimeRange, toPageQuery, toTableData, withTableSort } from '@/utils/ruoyi';
|
||||
import { toPageQuery, toTableData, withTableSort } from '@/utils/ruoyi';
|
||||
|
||||
function isImage(fileSuffix?: string) {
|
||||
return ['.png', '.jpg', '.jpeg'].includes((fileSuffix || '').toLowerCase());
|
||||
@ -37,6 +38,7 @@ export default function SystemOssPage() {
|
||||
const [uploadTitle, setUploadTitle] = useState('上传文件');
|
||||
const [uploadType, setUploadType] = useState<'file' | 'image'>('file');
|
||||
const [uploadValue, setUploadValue] = useState('');
|
||||
const { applyDateRange: applyCreateTimeDateRange } = useDateRangeQuery();
|
||||
|
||||
const canUpload = hasPermi(userInfo, ['system:oss:upload']);
|
||||
const canRemove = hasPermi(userInfo, ['system:oss:remove']);
|
||||
@ -149,9 +151,9 @@ export default function SystemOssPage() {
|
||||
rowSelection={{ selectedRowKeys: ids, onChange: (_, rows) => setSelectedRows(rows) }}
|
||||
request={async (params, sort) => {
|
||||
const { createTimeRange, ...tableParams } = params;
|
||||
const query = addDateRange(
|
||||
const query = applyCreateTimeDateRange(
|
||||
withTableSort(toPageQuery(tableParams), sort, { orderByColumn: 'createTime', isAsc: 'ascending' }),
|
||||
formatDateTimeRange(createTimeRange)
|
||||
createTimeRange
|
||||
);
|
||||
const preview = await getConfigKey('sys.oss.previewListResource');
|
||||
setPreviewListResource(preview.data === undefined ? true : preview.data === 'true');
|
||||
|
||||
@ -15,7 +15,6 @@ import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message, Popconfirm, Tag } from 'antd';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { DeptTreeVO } from '@/api/system/dept/types';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import type { PostForm, PostQuery, PostVO } from '@/api/system/post/types';
|
||||
import { addPost, delPost, getPost, listPost, postDeptTreeSelect, updatePost } from '@/api/system/post';
|
||||
import EllipsisText from '@/components/common/EllipsisText';
|
||||
@ -25,14 +24,12 @@ import { useDict } from '@/hooks/useDict';
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
const defaultPostForm: PostForm = { postSort: 0, status: '0' };
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
interface TreeSelectNode {
|
||||
title: string;
|
||||
|
||||
@ -19,19 +19,21 @@ import {
|
||||
} from '@ant-design/pro-components';
|
||||
import { history } from '@umijs/max';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message, Modal, Popconfirm, Switch } from 'antd';
|
||||
import { Button, Form, message, Popconfirm, Switch } from 'antd';
|
||||
import { useRef, useState } from 'react';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import type { RoleForm, RoleQuery, RoleVO } from '@/api/system/role/types';
|
||||
import { roleMenuTreeselect } from '@/api/system/menu';
|
||||
import { addRole, changeRoleStatus, delRole, getRole, listRole, updateRole } from '@/api/system/role';
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
import { useDateRangeQuery } from '@/hooks/useDateRangeQuery';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { confirmAction } from '@/utils/modal';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { addDateRange, formatDateTimeRange, toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
import RolePermissionModal from './components/RolePermissionModal';
|
||||
|
||||
const defaultRoleForm: RoleForm = {
|
||||
@ -44,24 +46,6 @@ const defaultRoleForm: RoleForm = {
|
||||
deptIds: []
|
||||
};
|
||||
|
||||
function confirmAction(content: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
Modal.confirm({
|
||||
title: '系统提示',
|
||||
content,
|
||||
onOk: () => resolve(),
|
||||
onCancel: () => reject(new Error('cancelled'))
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({
|
||||
label: item.dictLabel,
|
||||
value: item.dictValue
|
||||
}));
|
||||
}
|
||||
|
||||
export default function SystemRolePage() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
const [form] = Form.useForm<RoleForm>();
|
||||
@ -78,6 +62,7 @@ export default function SystemRolePage() {
|
||||
const [permissionOpen, { setTrue: openPermissionModal, setFalse: closePermissionModal }] = useBoolean(false);
|
||||
const [permissionRoleId, setPermissionRoleId] = useState<string | number>();
|
||||
const { updateExportParams, exportFile } = useTableExport();
|
||||
const { applyDateRange: applyCreateTimeDateRange } = useDateRangeQuery();
|
||||
|
||||
const canAdd = hasPermi(userInfo, ['system:role:add']);
|
||||
const canEdit = hasPermi(userInfo, ['system:role:edit']);
|
||||
@ -257,7 +242,7 @@ export default function SystemRolePage() {
|
||||
}}
|
||||
request={async params => {
|
||||
const { createTimeRange, ...tableParams } = params;
|
||||
const query = addDateRange(toPageQuery(tableParams), formatDateTimeRange(createTimeRange));
|
||||
const query = applyCreateTimeDateRange(toPageQuery(tableParams), createTimeRange);
|
||||
updateExportParams(query);
|
||||
const res = await listRole(query);
|
||||
return toTableData(res);
|
||||
|
||||
@ -32,12 +32,15 @@ import {
|
||||
} from '@/api/system/user';
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
import TreePanel from '@/components/common/TreePanel';
|
||||
import { useDateRangeQuery } from '@/hooks/useDateRangeQuery';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { confirmAction } from '@/utils/modal';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { addDateRange, formatDateTimeRange, toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
import { filterTree, toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
import UserDetailDrawer from './components/UserDetailDrawer';
|
||||
import UserFormModal from './components/UserFormModal';
|
||||
import UserImportModal from './components/UserImportModal';
|
||||
@ -55,17 +58,6 @@ interface TreeSelectNode {
|
||||
children?: TreeSelectNode[];
|
||||
}
|
||||
|
||||
function filterDisabledDept(depts: DeptTreeVO[]): DeptTreeVO[] {
|
||||
return depts.reduce<DeptTreeVO[]>((result, dept) => {
|
||||
if (dept.disabled) return result;
|
||||
result.push({
|
||||
...dept,
|
||||
children: dept.children?.length ? filterDisabledDept(dept.children) : []
|
||||
});
|
||||
return result;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function toTreeSelectData(depts: DeptTreeVO[]): TreeSelectNode[] {
|
||||
return depts.map(dept => ({
|
||||
title: dept.label,
|
||||
@ -75,23 +67,6 @@ function toTreeSelectData(depts: DeptTreeVO[]): TreeSelectNode[] {
|
||||
}));
|
||||
}
|
||||
|
||||
function dictOptions(dicts?: Array<{ dictLabel: string; dictValue: string }>) {
|
||||
return (dicts || []).map(item => ({
|
||||
label: item.dictLabel,
|
||||
value: item.dictValue
|
||||
}));
|
||||
}
|
||||
|
||||
function confirmAction(content: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
Modal.confirm({
|
||||
title: '系统提示',
|
||||
content,
|
||||
onOk: () => resolve(),
|
||||
onCancel: () => reject(new Error('cancelled'))
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default function SystemUserPage() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
@ -116,6 +91,7 @@ export default function SystemUserPage() {
|
||||
const [viewOpen, { setTrue: openViewDrawer, setFalse: closeViewDrawer }] = useBoolean(false);
|
||||
const [viewUserId, setViewUserId] = useState<string | number>();
|
||||
const { updateExportParams, exportFile } = useTableExport();
|
||||
const { applyDateRange: applyCreateTimeDateRange } = useDateRangeQuery();
|
||||
|
||||
const canAdd = hasPermi(userInfo, ['system:user:add']);
|
||||
const canEdit = hasPermi(userInfo, ['system:user:edit']);
|
||||
@ -127,7 +103,7 @@ export default function SystemUserPage() {
|
||||
useEffect(() => {
|
||||
Promise.all([deptTreeSelect(), getConfigKey('sys.user.initPassword')]).then(([deptRes, configRes]) => {
|
||||
setDeptOptions(deptRes.data || []);
|
||||
setEnabledDeptOptions(filterDisabledDept(deptRes.data || []));
|
||||
setEnabledDeptOptions(filterTree(deptRes.data || [], dept => !dept.disabled));
|
||||
setInitPassword(configRes.data || '');
|
||||
});
|
||||
}, []);
|
||||
@ -385,12 +361,12 @@ export default function SystemUserPage() {
|
||||
}}
|
||||
request={async params => {
|
||||
const { createTimeRange, ...tableParams } = params;
|
||||
const query = addDateRange(
|
||||
const query = applyCreateTimeDateRange(
|
||||
{
|
||||
...toPageQuery(tableParams),
|
||||
deptId
|
||||
},
|
||||
formatDateTimeRange(createTimeRange)
|
||||
createTimeRange
|
||||
);
|
||||
updateExportParams(query);
|
||||
const res = await listUser(query);
|
||||
|
||||
@ -21,7 +21,6 @@ import {
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import Cropper, { type ReactCropperElement } from 'react-cropper';
|
||||
import type { OnlineVO } from '@/api/monitor/online/types';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import type { SocialAuthVO } from '@/api/system/social/types';
|
||||
import type { UserForm, UserInfoVO } from '@/api/system/user/types';
|
||||
import { delOnline, getOnline } from '@/api/monitor/online';
|
||||
@ -35,6 +34,8 @@ import wechatIcon from '@/assets/icons/svg/wechat.svg';
|
||||
import defaultAvatar from '@/assets/images/profile.jpg';
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { usePermissionStore } from '@/stores/permissionStore';
|
||||
import { getUploadErrorMessage, validateUploadFile } from '@/utils/upload';
|
||||
|
||||
@ -70,9 +71,6 @@ function socialProviderNode(source?: string) {
|
||||
);
|
||||
}
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function normalizeOnlineDevices(payload: OnlineVO[] | { rows?: OnlineVO[] } | undefined) {
|
||||
if (Array.isArray(payload)) return payload;
|
||||
@ -87,8 +85,8 @@ export default function Profile() {
|
||||
const [profile, setProfile] = useState<UserInfoVO>();
|
||||
const [devices, setDevices] = useState<OnlineVO[]>([]);
|
||||
const [auths, setAuths] = useState<SocialAuthVO[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [avatarUploading, setAvatarUploading] = useState(false);
|
||||
const { loading, withLoading } = useLoading();
|
||||
const { loading: avatarUploading, withLoading: withAvatarUploading } = useLoading();
|
||||
const [avatarCropOpen, setAvatarCropOpen] = useState(false);
|
||||
const [avatarCropUrl, setAvatarCropUrl] = useState('');
|
||||
const [avatarFileName, setAvatarFileName] = useState('avatar.png');
|
||||
@ -97,17 +95,14 @@ export default function Profile() {
|
||||
const genderOptions = useMemo(() => dictOptions(dicts.sys_user_gender), [dicts.sys_user_gender]);
|
||||
|
||||
const loadProfile = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await withLoading(async () => {
|
||||
const [profileRes, authRes, onlineRes] = await Promise.all([getUserProfile(), getAuthList(), getOnline()]);
|
||||
setProfile(profileRes.data);
|
||||
setAuths(authRes.data || []);
|
||||
setDevices(normalizeOnlineDevices(onlineRes.data));
|
||||
userForm.setFieldsValue(profileRes.data.user || {});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [userForm]);
|
||||
});
|
||||
}, [userForm, withLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
loadProfile();
|
||||
@ -169,18 +164,17 @@ export default function Profile() {
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append('avatarfile', blob, avatarFileName);
|
||||
setAvatarUploading(true);
|
||||
try {
|
||||
await uploadAvatar(formData);
|
||||
message.success('修改成功');
|
||||
closeAvatarCrop();
|
||||
await reloadMenus();
|
||||
await loadProfile();
|
||||
} catch (error) {
|
||||
message.error(getUploadErrorMessage(error, '头像上传失败'));
|
||||
} finally {
|
||||
setAvatarUploading(false);
|
||||
}
|
||||
await withAvatarUploading(async () => {
|
||||
try {
|
||||
await uploadAvatar(formData);
|
||||
message.success('修改成功');
|
||||
closeAvatarCrop();
|
||||
await reloadMenus();
|
||||
await loadProfile();
|
||||
} catch (error) {
|
||||
message.error(getUploadErrorMessage(error, '头像上传失败'));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const deleteDevice = async (row: OnlineVO) => {
|
||||
|
||||
@ -17,10 +17,11 @@ import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
import type { TableQuery, TableVO } from '@/api/tool/gen/types';
|
||||
import { batchGenCode, delTable, getDataNames, listTable, previewTable, synchDb } from '@/api/tool/gen';
|
||||
import EllipsisText from '@/components/common/EllipsisText';
|
||||
import { useDateRangeQuery } from '@/hooks/useDateRangeQuery';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { saveValidatedBlob } from '@/utils/download';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { addDateRange, toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
import ImportTableModal from './components/ImportTableModal';
|
||||
|
||||
function previewName(path: string) {
|
||||
@ -63,6 +64,7 @@ export default function ToolGenPage() {
|
||||
const [currentPage, setCurrentPage] = useState(
|
||||
() => Number(new URLSearchParams(location.search).get('pageNum')) || 1
|
||||
);
|
||||
const { applyDateRange } = useDateRangeQuery();
|
||||
|
||||
const canCode = hasPermi(userInfo, ['tool:gen:code']);
|
||||
const canImport = hasPermi(userInfo, ['tool:gen:import']);
|
||||
@ -238,7 +240,7 @@ export default function ToolGenPage() {
|
||||
const { dateRange, ...tableParams } = params;
|
||||
setCurrentPage(tableParams.current || 1);
|
||||
setCurrentDataName(tableParams.dataName || undefined);
|
||||
const query = addDateRange(toPageQuery(tableParams), dateRange);
|
||||
const query = applyDateRange(toPageQuery(tableParams), dateRange);
|
||||
const res = await listTable(query);
|
||||
return toTableData(res);
|
||||
}}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { Key } from 'react';
|
||||
import { DeleteOutlined, EditOutlined, PlusOutlined, SortAscendingOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
ModalForm,
|
||||
@ -16,6 +15,7 @@ import { useMemo, useRef, useState } from 'react';
|
||||
import type { CategoryForm, CategoryQuery, CategoryVO } from '@/api/workflow/category/types';
|
||||
import { addCategory, delCategory, getCategory, listCategory, updateCategory } from '@/api/workflow/category';
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
import { useTreeTableExpand } from '@/hooks/useTreeTableExpand';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { handleTree } from '@/utils/ruoyi';
|
||||
@ -36,18 +36,14 @@ function toTreeSelectData(nodes: CategoryVO[]): CategorySelectNode[] {
|
||||
}));
|
||||
}
|
||||
|
||||
function collectCategoryKeys(nodes: CategoryVO[]): Key[] {
|
||||
return nodes.flatMap(node => [node.categoryId, ...(node.children ? collectCategoryKeys(node.children) : [])]);
|
||||
}
|
||||
|
||||
export default function WorkflowCategoryPage() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
const [form] = Form.useForm<CategoryForm>();
|
||||
const userInfo = useUserStore(state => state.userInfo);
|
||||
const [categoryOptions, setCategoryOptions] = useState<CategoryVO[]>([]);
|
||||
const [tableRows, setTableRows] = useState<CategoryVO[]>([]);
|
||||
const [expandAll, setExpandAll] = useState(false);
|
||||
const [expandedRowKeys, setExpandedRowKeys] = useState<Key[]>([]);
|
||||
const { expandAll, expandedRowKeys, onExpandedRowsChange, syncExpandedRows, toggleExpandAll } =
|
||||
useTreeTableExpand<CategoryVO>(row => row.categoryId);
|
||||
const [modalOpen, { setTrue: openModal, setFalse: closeModal }] = useBoolean(false);
|
||||
const [modalTitle, setModalTitle] = useState('');
|
||||
|
||||
@ -131,13 +127,13 @@ export default function WorkflowCategoryPage() {
|
||||
search={{ labelWidth: 90 }}
|
||||
expandable={{
|
||||
expandedRowKeys,
|
||||
onExpandedRowsChange: keys => setExpandedRowKeys([...keys])
|
||||
onExpandedRowsChange
|
||||
}}
|
||||
request={async params => {
|
||||
const res = await listCategory({ categoryName: params.categoryName });
|
||||
const rows = handleTree<CategoryVO>(res.data || [], 'categoryId', 'parentId');
|
||||
setTableRows(rows);
|
||||
setExpandedRowKeys(expandAll ? collectCategoryKeys(rows) : []);
|
||||
syncExpandedRows(rows, expandAll);
|
||||
return { data: rows, total: rows.length, success: true };
|
||||
}}
|
||||
toolbar={{ title: '流程分类列表' }}
|
||||
@ -150,13 +146,7 @@ export default function WorkflowCategoryPage() {
|
||||
<Button
|
||||
key="expand"
|
||||
icon={<SortAscendingOutlined />}
|
||||
onClick={() => {
|
||||
setExpandAll(value => {
|
||||
const next = !value;
|
||||
setExpandedRowKeys(next ? collectCategoryKeys(tableRows) : []);
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
onClick={() => toggleExpandAll(tableRows)}
|
||||
>
|
||||
展开/折叠
|
||||
</Button>
|
||||
|
||||
@ -10,7 +10,6 @@ import { PageContainer, ProTable, type ActionType, type ProColumns } from '@ant-
|
||||
import { history } from '@umijs/max';
|
||||
import { Button, message, Tag } from 'antd';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import type { LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
|
||||
import { cancelProcessApply } from '@/api/workflow/instance';
|
||||
import { delLeave, listLeave } from '@/api/workflow/leave';
|
||||
@ -21,6 +20,7 @@ import { useDict } from '@/hooks/useDict';
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
@ -31,9 +31,6 @@ const leaveTypeOptions = [
|
||||
{ value: '4', label: '婚假' }
|
||||
];
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function leaveTypeLabel(value?: string) {
|
||||
return leaveTypeOptions.find(item => item.value === value)?.label || value || '-';
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
import { PageContainer, ProTable, type ActionType, type ProColumns } from '@ant-design/pro-components';
|
||||
import { history, useLocation } from '@umijs/max';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message, Modal, Popconfirm, Space, Switch, Tabs, Tag } from 'antd';
|
||||
import { Button, Form, message, Popconfirm, Space, Switch, Tabs, Tag } from 'antd';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import type { CategoryTreeVO } from '@/api/workflow/category/types';
|
||||
@ -31,8 +31,10 @@ import {
|
||||
} from '@/api/workflow/definition';
|
||||
import EllipsisText from '@/components/common/EllipsisText';
|
||||
import TreePanel from '@/components/common/TreePanel';
|
||||
import { useSearchReset } from '@/hooks/useSearchReset';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { saveValidatedBlob } from '@/utils/download';
|
||||
import { confirmAction } from '@/utils/modal';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
import DefinitionFormModal from './components/DefinitionFormModal';
|
||||
@ -59,17 +61,6 @@ const defaultForm: FlowDefinitionForm = {
|
||||
modelValue: 'CLASSICS'
|
||||
};
|
||||
|
||||
function confirmAction(content: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
Modal.confirm({
|
||||
title: '系统提示',
|
||||
content,
|
||||
onOk: () => resolve(),
|
||||
onCancel: () => reject(new Error('cancelled'))
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function requestDefinitionList(
|
||||
tab: DefinitionTab,
|
||||
query: FlowDefinitionQuery
|
||||
@ -104,6 +95,10 @@ export default function WorkflowProcessDefinitionPage() {
|
||||
|
||||
const selectedIds = selectedRows.map(item => item.id).filter(Boolean);
|
||||
const selectedOne = selectedRows.length === 1 ? selectedRows[0] : undefined;
|
||||
const resetSearch = useSearchReset(
|
||||
actionRef,
|
||||
useCallback(() => setCategory(undefined), [])
|
||||
);
|
||||
|
||||
const switchTab = useCallback((nextTab: DefinitionTab) => {
|
||||
setSelectedRows([]);
|
||||
@ -224,11 +219,6 @@ export default function WorkflowProcessDefinitionPage() {
|
||||
await saveValidatedBlob(blob, `${selectedOne.flowCode}.json`);
|
||||
};
|
||||
|
||||
const resetSearch = () => {
|
||||
setCategory(undefined);
|
||||
setTimeout(() => actionRef.current?.reloadAndRest?.(), 0);
|
||||
};
|
||||
|
||||
const columns: ProColumns<FlowDefinitionVO>[] = [
|
||||
{
|
||||
title: '流程定义名称',
|
||||
|
||||
@ -9,8 +9,7 @@ import {
|
||||
} from '@ant-design/pro-components';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Badge, Button, Card, Form, Input, message, Modal, Popconfirm, Tabs, Tag } from 'antd';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { UserVO } from '@/api/system/user/types';
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import type { CategoryTreeVO } from '@/api/workflow/category/types';
|
||||
@ -33,15 +32,16 @@ import RowActions from '@/components/common/RowActions';
|
||||
import TreePanel from '@/components/common/TreePanel';
|
||||
import UserSelect from '@/components/common/UserSelect';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { useSearchReset } from '@/hooks/useSearchReset';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { confirmAction } from '@/utils/modal';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
type InstanceTab = 'running' | 'finish';
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function requestInstanceList(tab: InstanceTab, query: FlowInstanceQuery): Promise<R<PageResult<FlowInstanceVO>>> {
|
||||
if (tab === 'running') return pageByRunning(query);
|
||||
@ -58,17 +58,6 @@ function openBusinessForm(row: FlowInstanceVO) {
|
||||
});
|
||||
}
|
||||
|
||||
function confirmAction(content: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
Modal.confirm({
|
||||
title: '系统提示',
|
||||
content,
|
||||
onOk: () => resolve(),
|
||||
onCancel: () => reject(new Error('cancelled'))
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default function WorkflowProcessInstancePage() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
const [invalidForm] = Form.useForm<{ comment?: string }>();
|
||||
@ -84,7 +73,7 @@ export default function WorkflowProcessInstancePage() {
|
||||
const [invalidOpen, { setTrue: openInvalidModal, setFalse: closeInvalidModal }] = useBoolean(false);
|
||||
const [invalidRow, setInvalidRow] = useState<FlowInstanceVO>();
|
||||
const [variableOpen, { setTrue: openVariableModal, setFalse: closeVariableModal }] = useBoolean(false);
|
||||
const [variableLoading, setVariableLoading] = useState(false);
|
||||
const { loading: variableLoading, withLoading: withVariableLoading } = useLoading();
|
||||
const [variableRow, setVariableRow] = useState<FlowInstanceVO>();
|
||||
const [variableText, setVariableText] = useState('');
|
||||
|
||||
@ -97,6 +86,13 @@ export default function WorkflowProcessInstancePage() {
|
||||
const businessStatusOptions = useMemo(() => dictOptions(dicts.wf_business_status), [dicts.wf_business_status]);
|
||||
const selectedIds = selectedRows.map(item => item.id).filter(Boolean);
|
||||
const selectedApplicantIds = selectedApplicants.map(item => item.userId).filter(Boolean) as Array<string | number>;
|
||||
const resetSearch = useSearchReset(
|
||||
actionRef,
|
||||
useCallback(() => {
|
||||
setCategory(undefined);
|
||||
setSelectedApplicants([]);
|
||||
}, [])
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
categoryTree().then(res => setCategoryOptions(res.data || []));
|
||||
@ -123,12 +119,6 @@ export default function WorkflowProcessInstancePage() {
|
||||
actionRef.current?.reloadAndRest?.();
|
||||
};
|
||||
|
||||
const resetSearch = () => {
|
||||
setCategory(undefined);
|
||||
setSelectedApplicants([]);
|
||||
setTimeout(() => actionRef.current?.reloadAndRest?.(), 0);
|
||||
};
|
||||
|
||||
const handleDelete = async (row?: FlowInstanceVO) => {
|
||||
const ids = row?.id ? [row.id] : selectedIds;
|
||||
if (!ids.length) return;
|
||||
@ -160,14 +150,11 @@ export default function WorkflowProcessInstancePage() {
|
||||
const openVariable = async (row: FlowInstanceVO) => {
|
||||
setVariableRow(row);
|
||||
openVariableModal();
|
||||
setVariableLoading(true);
|
||||
variableForm.resetFields();
|
||||
try {
|
||||
await withVariableLoading(async () => {
|
||||
const res = await instanceVariable(row.id);
|
||||
setVariableText(res.data.variable || '');
|
||||
} finally {
|
||||
setVariableLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const submitVariable = async () => {
|
||||
|
||||
@ -12,7 +12,6 @@ import {
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message, Popconfirm } from 'antd';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import type { SpelForm, SpelQuery, SpelVO } from '@/api/workflow/spel/types';
|
||||
import { addSpel, delSpel, getSpel, listSpel, updateSpel } from '@/api/workflow/spel';
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
@ -21,15 +20,13 @@ import RowActions from '@/components/common/RowActions';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
const defaultSpelForm: SpelForm = { status: '0' };
|
||||
const spelPlaceholder = '例如:#{@组件名.方法名(#方法参数)} 或 ${方法参数}';
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function buildViewSpel(values: Pick<SpelForm, 'componentName' | 'methodName' | 'methodParams'>) {
|
||||
const comp = (values.componentName || '').trim();
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { BellOutlined, EyeOutlined, SettingOutlined, SwapOutlined, UserAddOutlined } from '@ant-design/icons';
|
||||
import { ModalForm, PageContainer, ProTable, type ActionType, type ProColumns } from '@ant-design/pro-components';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Badge, Button, Form, message, Modal, Tabs } from 'antd';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import { Badge, Button, Form, message, Tabs } from 'antd';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import type { UserVO } from '@/api/system/user/types';
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import type { FlowTaskVO, TaskQuery } from '@/api/workflow/task/types';
|
||||
@ -17,13 +16,13 @@ import MessageType from '@/components/workflow/MessageType';
|
||||
import ProcessMeddle from '@/components/workflow/ProcessMeddle';
|
||||
import UserNameDisplay from '@/components/workflow/UserNameDisplay';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useSearchReset } from '@/hooks/useSearchReset';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { confirmTitleSafe } from '@/utils/modal';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
type AllTaskTab = 'waiting' | 'finish';
|
||||
type UserSelectMode = 'applicant' | 'assignee';
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function requestAllTaskList(tab: AllTaskTab, query: TaskQuery): Promise<R<PageResult<FlowTaskVO>>> {
|
||||
if (tab === 'waiting') return pageByAllTaskWait(query);
|
||||
@ -40,16 +39,6 @@ function openBusinessForm(row: FlowTaskVO) {
|
||||
});
|
||||
}
|
||||
|
||||
function confirmAction(title: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
Modal.confirm({
|
||||
title,
|
||||
onOk: () => resolve(),
|
||||
onCancel: () => reject(new Error('cancelled'))
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default function WorkflowAllTaskWaitingPage() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
const [urgeForm] = Form.useForm<{ message: string; messageType: string[] }>();
|
||||
@ -67,6 +56,10 @@ export default function WorkflowAllTaskWaitingPage() {
|
||||
const taskStatusOptions = useMemo(() => dictOptions(dicts.wf_task_status), [dicts.wf_task_status]);
|
||||
const selectedTaskIds = selectedRows.map(item => item.id).filter(Boolean);
|
||||
const selectedApplicantIds = selectedApplicants.map(item => item.userId).filter(Boolean) as Array<string | number>;
|
||||
const resetSearch = useSearchReset(
|
||||
actionRef,
|
||||
useCallback(() => setSelectedApplicants([]), [])
|
||||
);
|
||||
|
||||
const changeTab = (key: string) => {
|
||||
setActiveTab(key as AllTaskTab);
|
||||
@ -103,16 +96,12 @@ export default function WorkflowAllTaskWaitingPage() {
|
||||
return;
|
||||
}
|
||||
const userId = user.userId;
|
||||
Modal.confirm({
|
||||
title: '是否确认提交?',
|
||||
onOk: async () => {
|
||||
await updateAssignee(selectedTaskIds, userId);
|
||||
message.success('操作成功');
|
||||
closeUserModal();
|
||||
setSelectedRows([]);
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
});
|
||||
if (!(await confirmTitleSafe('是否确认提交?'))) return;
|
||||
await updateAssignee(selectedTaskIds, userId);
|
||||
message.success('操作成功');
|
||||
closeUserModal();
|
||||
setSelectedRows([]);
|
||||
actionRef.current?.reload();
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -122,13 +111,8 @@ export default function WorkflowAllTaskWaitingPage() {
|
||||
actionRef.current?.reloadAndRest?.();
|
||||
};
|
||||
|
||||
const resetSearch = () => {
|
||||
setSelectedApplicants([]);
|
||||
setTimeout(() => actionRef.current?.reloadAndRest?.(), 0);
|
||||
};
|
||||
|
||||
const submitUrge = async (values: { message: string; messageType: string[] }) => {
|
||||
await confirmAction('是否确认提交?');
|
||||
if (!(await confirmTitleSafe('是否确认提交?'))) return false;
|
||||
await urgeTask({ ...values, taskIdList: selectedTaskIds });
|
||||
message.success('操作成功');
|
||||
urgeForm.resetFields();
|
||||
|
||||
@ -2,7 +2,6 @@ import { DeleteOutlined, EditOutlined, EyeOutlined, RollbackOutlined } from '@an
|
||||
import { PageContainer, ProTable, type ActionType, type ProColumns } from '@ant-design/pro-components';
|
||||
import { message, Tag } from 'antd';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import type { CategoryTreeVO } from '@/api/workflow/category/types';
|
||||
import type { FlowInstanceQuery, FlowInstanceVO } from '@/api/workflow/instance/types';
|
||||
import { categoryTree } from '@/api/workflow/category';
|
||||
@ -14,12 +13,10 @@ import RowActions from '@/components/common/RowActions';
|
||||
import TreePanel from '@/components/common/TreePanel';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function editableStatus(status?: string) {
|
||||
return status === 'draft' || status === 'cancel' || status === 'back';
|
||||
|
||||
@ -3,7 +3,6 @@ import { PageContainer, ProTable, type ActionType, type ProColumns } from '@ant-
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Badge, Button } from 'antd';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
import type { UserVO } from '@/api/system/user/types';
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import type { FlowTaskVO, TaskQuery } from '@/api/workflow/task/types';
|
||||
@ -15,6 +14,7 @@ import RowActions from '@/components/common/RowActions';
|
||||
import UserSelect from '@/components/common/UserSelect';
|
||||
import UserNameDisplay from '@/components/workflow/UserNameDisplay';
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
import { toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
type TaskListType = 'wait' | 'finish' | 'copy';
|
||||
@ -29,9 +29,6 @@ const pageTitle: Record<TaskListType, string> = {
|
||||
copy: '抄送任务'
|
||||
};
|
||||
|
||||
function dictOptions(dicts?: DictData[]) {
|
||||
return (dicts || []).map(item => ({ label: item.dictLabel, value: item.dictValue }));
|
||||
}
|
||||
|
||||
function requestTaskList(type: TaskListType, query: TaskQuery): Promise<R<PageResult<FlowTaskVO>>> {
|
||||
if (type === 'wait') return pageByTaskWait(query);
|
||||
|
||||
9
src/utils/dict.ts
Normal file
9
src/utils/dict.ts
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
import type { DictData } from '@/api/system/dict/data/types';
|
||||
|
||||
export function dictOptions(dicts?: Pick<DictData, 'dictLabel' | 'dictValue'>[]) {
|
||||
return (dicts || []).map(item => ({
|
||||
label: item.dictLabel,
|
||||
value: item.dictValue
|
||||
}));
|
||||
}
|
||||
35
src/utils/modal.ts
Normal file
35
src/utils/modal.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { Modal, type ModalFuncProps } from 'antd';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
const CANCELLED = new Error('cancelled');
|
||||
|
||||
export function confirmModal(options: ModalFuncProps) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
Modal.confirm({
|
||||
title: '系统提示',
|
||||
...options,
|
||||
onOk: () => resolve(),
|
||||
onCancel: () => reject(CANCELLED)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function confirmAction(content: ReactNode, options?: Omit<ModalFuncProps, 'content' | 'onOk' | 'onCancel'>) {
|
||||
return confirmModal({ content, ...options });
|
||||
}
|
||||
|
||||
export function confirmTitle(title: ReactNode, options?: Omit<ModalFuncProps, 'title' | 'onOk' | 'onCancel'>) {
|
||||
return confirmModal({ title, ...options });
|
||||
}
|
||||
|
||||
export async function confirmTitleSafe(
|
||||
title: ReactNode,
|
||||
options?: Omit<ModalFuncProps, 'title' | 'onOk' | 'onCancel'>
|
||||
) {
|
||||
try {
|
||||
await confirmTitle(title, options);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import dayjs, { type ConfigType } from 'dayjs';
|
||||
import type { Key } from 'react';
|
||||
|
||||
export function tansParams(params: Record<string, unknown>) {
|
||||
let result = '';
|
||||
@ -35,14 +36,20 @@ export function parseStrEmpty(value?: string | number) {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function addDateRange<T extends Record<string, unknown>>(params: T, dateRange?: [string, string] | null) {
|
||||
export function addDateRange<T extends Record<string, unknown>>(
|
||||
params: T,
|
||||
dateRange?: [string, string] | null,
|
||||
propName?: string
|
||||
) {
|
||||
if (!dateRange?.length) return params;
|
||||
const beginKey = propName ? `begin${propName}` : 'beginTime';
|
||||
const endKey = propName ? `end${propName}` : 'endTime';
|
||||
return {
|
||||
...params,
|
||||
params: {
|
||||
...((params.params as Record<string, unknown>) || {}),
|
||||
beginTime: dateRange[0],
|
||||
endTime: dateRange[1]
|
||||
[beginKey]: dateRange[0],
|
||||
[endKey]: dateRange[1]
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -61,6 +68,35 @@ export function formatDateTimeRange(dateRange?: [ConfigType, ConfigType] | null)
|
||||
];
|
||||
}
|
||||
|
||||
export function toDayjsValue(value?: ConfigType | null) {
|
||||
return value ? dayjs(value) : undefined;
|
||||
}
|
||||
|
||||
export function formatDateTimeValue(value: unknown) {
|
||||
if (!value) return value;
|
||||
return dayjs(value as ConfigType).format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
|
||||
export function toDayjsFields<T extends Record<string, unknown>>(data: T, fields: string[]) {
|
||||
const next: Record<string, unknown> = { ...data };
|
||||
for (const field of fields) {
|
||||
if (next[field]) {
|
||||
next[field] = toDayjsValue(next[field] as ConfigType);
|
||||
}
|
||||
}
|
||||
return next as T;
|
||||
}
|
||||
|
||||
export function formatDateTimeFields<T extends Record<string, unknown>>(data: T, fields: string[]) {
|
||||
const next: Record<string, unknown> = { ...data };
|
||||
for (const field of fields) {
|
||||
if (next[field]) {
|
||||
next[field] = formatDateTimeValue(next[field]);
|
||||
}
|
||||
}
|
||||
return next as T;
|
||||
}
|
||||
|
||||
export function toPageQuery<T extends object>(params: T & { current?: number; pageSize?: number }) {
|
||||
const { current, pageSize, ...rest } = params;
|
||||
return {
|
||||
@ -138,3 +174,30 @@ export function handleTree<T>(data: T[], id = 'id', parentId = 'parentId', child
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
export function collectTreeKeys<T extends object>(
|
||||
nodes: T[],
|
||||
getKey: (node: T) => Key,
|
||||
getChildren: (node: T) => T[] | undefined = node => (node as Record<string, unknown>).children as T[] | undefined
|
||||
): Key[] {
|
||||
return nodes.flatMap(node => {
|
||||
const children = getChildren(node);
|
||||
return [getKey(node), ...(children?.length ? collectTreeKeys(children, getKey, getChildren) : [])];
|
||||
});
|
||||
}
|
||||
|
||||
export function filterTree<T extends object>(
|
||||
nodes: T[],
|
||||
predicate: (node: T) => boolean,
|
||||
children = 'children'
|
||||
): T[] {
|
||||
return nodes
|
||||
.filter(predicate)
|
||||
.map(node => {
|
||||
const record = node as Record<string, unknown>;
|
||||
return {
|
||||
...node,
|
||||
[children]: filterTree((record[children] as T[] | undefined) || [], predicate, children)
|
||||
} as T;
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user