336 lines
9.1 KiB
TypeScript
336 lines
9.1 KiB
TypeScript
import {
|
|
addTask,
|
|
queryTaskList,
|
|
removeTask,
|
|
updateTask,
|
|
} from '@/services/demo/task';
|
|
import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
|
|
import type { ActionType, ProColumns } from '@ant-design/pro-components';
|
|
import {
|
|
ModalForm,
|
|
PageContainer,
|
|
ProFormSelect,
|
|
ProFormText,
|
|
ProTable,
|
|
} from '@ant-design/pro-components';
|
|
import { useIntl } from '@umijs/max';
|
|
import { Button, Tag, message } from 'antd';
|
|
import { useRef, useState } from 'react';
|
|
import type { TaskItem } from './data';
|
|
|
|
export default () => {
|
|
const actionRef = useRef<ActionType>();
|
|
const [createModalOpen, handleModalOpen] = useState<boolean>(false);
|
|
const [currentRow, setCurrentRow] = useState<TaskItem>();
|
|
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
|
|
const intl = useIntl();
|
|
|
|
const columns: ProColumns<TaskItem>[] = [
|
|
{
|
|
title: intl.formatMessage({
|
|
id: 'pages.demo.task.id',
|
|
defaultMessage: 'Task ID',
|
|
}),
|
|
dataIndex: 'id',
|
|
hideInSearch: true,
|
|
width: 80,
|
|
},
|
|
{
|
|
title: intl.formatMessage({
|
|
id: 'pages.demo.task.title',
|
|
defaultMessage: 'Title',
|
|
}),
|
|
dataIndex: 'title',
|
|
copyable: true,
|
|
ellipsis: true,
|
|
tip: 'The task title is unique key',
|
|
formItemProps: {
|
|
rules: [
|
|
{
|
|
required: true,
|
|
message:
|
|
intl.formatMessage({
|
|
id: 'pages.demo.task.title',
|
|
defaultMessage: 'Title',
|
|
}) + ' is required',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
title: intl.formatMessage({
|
|
id: 'pages.demo.task.status',
|
|
defaultMessage: 'Status',
|
|
}),
|
|
dataIndex: 'status',
|
|
filters: true,
|
|
onFilter: true,
|
|
valueEnum: {
|
|
todo: {
|
|
text: intl.formatMessage({
|
|
id: 'pages.demo.task.status.todo',
|
|
defaultMessage: 'To Do',
|
|
}),
|
|
status: 'Default',
|
|
},
|
|
doing: {
|
|
text: intl.formatMessage({
|
|
id: 'pages.demo.task.status.doing',
|
|
defaultMessage: 'Doing',
|
|
}),
|
|
status: 'Processing',
|
|
},
|
|
done: {
|
|
text: intl.formatMessage({
|
|
id: 'pages.demo.task.status.done',
|
|
defaultMessage: 'Done',
|
|
}),
|
|
status: 'Success',
|
|
},
|
|
},
|
|
render: (_, record) => {
|
|
const colorMap: Record<string, string> = {
|
|
todo: 'default',
|
|
doing: 'orange',
|
|
done: 'success',
|
|
};
|
|
// handle undefined status
|
|
const color = colorMap[record.status || 'todo'] || 'default';
|
|
const statusText = record.status
|
|
? intl.formatMessage({
|
|
id: `pages.demo.task.status.${record.status}`,
|
|
defaultMessage: record.status.toUpperCase(),
|
|
})
|
|
: '-';
|
|
return <Tag color={color}>{statusText}</Tag>;
|
|
},
|
|
},
|
|
{
|
|
title: intl.formatMessage({
|
|
id: 'pages.demo.task.priority',
|
|
defaultMessage: 'Priority',
|
|
}),
|
|
dataIndex: 'priority',
|
|
valueType: 'digit',
|
|
sorter: true,
|
|
hideInForm: true,
|
|
hideInSearch: true,
|
|
},
|
|
{
|
|
title: intl.formatMessage({
|
|
id: 'pages.demo.task.createdAt',
|
|
defaultMessage: 'Created At',
|
|
}),
|
|
dataIndex: 'createdAt',
|
|
valueType: 'dateTime',
|
|
sorter: true,
|
|
hideInSearch: true,
|
|
hideInForm: true,
|
|
},
|
|
{
|
|
title: intl.formatMessage({
|
|
id: 'pages.demo.task.action',
|
|
defaultMessage: 'Action',
|
|
}),
|
|
dataIndex: 'option',
|
|
valueType: 'option',
|
|
render: (_, record) => [
|
|
<Button
|
|
key="edit"
|
|
size="small"
|
|
type="text"
|
|
icon={<EditOutlined />}
|
|
onClick={() => {
|
|
setCurrentRow(record);
|
|
handleUpdateModalOpen(true);
|
|
}}
|
|
>
|
|
{intl.formatMessage({
|
|
id: 'pages.demo.task.edit',
|
|
defaultMessage: 'Edit',
|
|
})}
|
|
</Button>,
|
|
<Button
|
|
key="delete"
|
|
size="small"
|
|
type="text"
|
|
danger
|
|
icon={<DeleteOutlined />}
|
|
onClick={async () => {
|
|
await removeTask({ id: record.id });
|
|
actionRef.current?.reload();
|
|
message.success('Deleted successfully');
|
|
}}
|
|
>
|
|
{intl.formatMessage({
|
|
id: 'pages.demo.task.delete',
|
|
defaultMessage: 'Delete',
|
|
})}
|
|
</Button>,
|
|
],
|
|
},
|
|
];
|
|
|
|
return (
|
|
<PageContainer>
|
|
<ProTable<TaskItem>
|
|
headerTitle={intl.formatMessage({
|
|
id: 'menu.demo.task-board',
|
|
defaultMessage: 'Task List',
|
|
})}
|
|
actionRef={actionRef}
|
|
rowKey="id"
|
|
form={{
|
|
layout: 'vertical',
|
|
}}
|
|
search={{
|
|
labelWidth: 'auto',
|
|
}}
|
|
toolBarRender={() => [
|
|
<Button
|
|
type="primary"
|
|
key="primary"
|
|
onClick={() => {
|
|
handleModalOpen(true);
|
|
}}
|
|
>
|
|
<PlusOutlined />{' '}
|
|
{intl.formatMessage({
|
|
id: 'pages.product.add',
|
|
defaultMessage: 'New Task',
|
|
})}
|
|
</Button>,
|
|
]}
|
|
request={async (params) => {
|
|
// ProTable already integrates useRequest logic internally when 'request' is provided
|
|
const msg = await queryTaskList({ ...params });
|
|
return {
|
|
data: msg.data,
|
|
success: msg.success,
|
|
total: msg.total,
|
|
};
|
|
}}
|
|
columns={columns}
|
|
/>
|
|
|
|
{/* Create Form */}
|
|
<ModalForm
|
|
title={intl.formatMessage({
|
|
id: 'pages.product.add',
|
|
defaultMessage: 'New Task',
|
|
})}
|
|
width="400px"
|
|
layout="vertical"
|
|
labelCol={{ span: 24 }}
|
|
wrapperCol={{ span: 24 }}
|
|
open={createModalOpen}
|
|
onOpenChange={handleModalOpen}
|
|
onFinish={async (value) => {
|
|
await addTask(value as TaskItem);
|
|
message.success('Added successfully');
|
|
handleModalOpen(false);
|
|
actionRef.current?.reload();
|
|
return true;
|
|
}}
|
|
>
|
|
<ProFormText
|
|
rules={[{ required: true, message: 'Title is required' }]}
|
|
name="title"
|
|
label={intl.formatMessage({
|
|
id: 'pages.demo.task.title',
|
|
defaultMessage: 'Title',
|
|
})}
|
|
/>
|
|
<ProFormSelect
|
|
name="status"
|
|
label={intl.formatMessage({
|
|
id: 'pages.demo.task.status',
|
|
defaultMessage: 'Status',
|
|
})}
|
|
valueEnum={{
|
|
todo: intl.formatMessage({
|
|
id: 'pages.demo.task.status.todo',
|
|
defaultMessage: 'To Do',
|
|
}),
|
|
doing: intl.formatMessage({
|
|
id: 'pages.demo.task.status.doing',
|
|
defaultMessage: 'Doing',
|
|
}),
|
|
done: intl.formatMessage({
|
|
id: 'pages.demo.task.status.done',
|
|
defaultMessage: 'Done',
|
|
}),
|
|
}}
|
|
placeholder="Please select a status"
|
|
rules={[{ required: true, message: 'Please select your status!' }]}
|
|
/>
|
|
</ModalForm>
|
|
|
|
{/* Edit Form */}
|
|
<ModalForm
|
|
title={intl.formatMessage({
|
|
id: 'pages.product.edit',
|
|
defaultMessage: 'Edit Task',
|
|
})}
|
|
width="400px"
|
|
layout="vertical"
|
|
labelCol={{ span: 24 }}
|
|
wrapperCol={{ span: 24 }}
|
|
open={updateModalOpen}
|
|
onOpenChange={handleUpdateModalOpen}
|
|
onFinish={async (value) => {
|
|
const data = { ...currentRow, ...value };
|
|
await updateTask(data as TaskItem);
|
|
message.success('Updated successfully');
|
|
handleUpdateModalOpen(false);
|
|
actionRef.current?.reload();
|
|
return true;
|
|
}}
|
|
initialValues={currentRow}
|
|
modalProps={{
|
|
destroyOnClose: true,
|
|
}}
|
|
>
|
|
<ProFormText
|
|
name="id"
|
|
label={intl.formatMessage({
|
|
id: 'pages.demo.task.id',
|
|
defaultMessage: 'ID',
|
|
})}
|
|
disabled
|
|
hidden
|
|
/>
|
|
<ProFormText
|
|
name="title"
|
|
label={intl.formatMessage({
|
|
id: 'pages.demo.task.title',
|
|
defaultMessage: 'Title',
|
|
})}
|
|
rules={[{ required: true, message: 'Title is required' }]}
|
|
/>
|
|
<ProFormSelect
|
|
name="status"
|
|
label={intl.formatMessage({
|
|
id: 'pages.demo.task.status',
|
|
defaultMessage: 'Status',
|
|
})}
|
|
valueEnum={{
|
|
todo: intl.formatMessage({
|
|
id: 'pages.demo.task.status.todo',
|
|
defaultMessage: 'To Do',
|
|
}),
|
|
doing: intl.formatMessage({
|
|
id: 'pages.demo.task.status.doing',
|
|
defaultMessage: 'Doing',
|
|
}),
|
|
done: intl.formatMessage({
|
|
id: 'pages.demo.task.status.done',
|
|
defaultMessage: 'Done',
|
|
}),
|
|
}}
|
|
/>
|
|
</ModalForm>
|
|
</PageContainer>
|
|
);
|
|
};
|