feat: add TaskBoard demo and refactor project structure
This commit is contained in:
335
src/pages/Demo/TaskBoard/index.tsx
Normal file
335
src/pages/Demo/TaskBoard/index.tsx
Normal file
@@ -0,0 +1,335 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user