feat:列表ok

This commit is contained in:
shixiaohua 2024-04-16 16:11:21 +08:00
parent c5abcf2166
commit 8ab2e8d69c
10 changed files with 706 additions and 208 deletions

View File

@ -29,12 +29,25 @@ export type ResponseVO<T>={
export type DataType ={ export type DataType ={
key: React.ReactNode; key: React.ReactNode;
id: number; id: number;
pid:number;
pPid:number;
code: string; code: string;
name: string; name: string;
description: string; description: string;
state: number; state: number|string|undefined;
priority: number; priority: number|string|undefined;
type:number; type:number;
action?:React.ReactNode; action?:React.ReactNode;
children: DataType[]; expectedStartTime?:Date;
expectedEndTime?:Date;
actualStartTime?:Date;
actualEndTime?:Date;
children: DataType[]|undefined;
}
export type DictType={
id:number;
code:string
name:string;
order:number;
color:string;
} }

View File

@ -1,8 +1,8 @@
import {unstable_noStore as noStore} from 'next/cache'; import {unstable_noStore as noStore} from 'next/cache';
import axios, {AxiosResponse} from "axios"; import axios, {AxiosResponse} from "axios";
import {DataType, ResponseVO, ResultPage} from "@/app/lib/definitions"; import {DataType, DictType, ResponseVO, ResultPage} from "@/app/lib/definitions";
export async function taskTreeResult():Promise<ResponseVO<ResultPage<DataType>>> { export async function taskTreeResult(): Promise<ResponseVO<ResultPage<DataType>>> {
noStore(); noStore();
try { try {
// 使用 Axios 发送 POST 请求获取数据 // 使用 Axios 发送 POST 请求获取数据
@ -19,3 +19,207 @@ export async function taskTreeResult():Promise<ResponseVO<ResultPage<DataType>>>
throw new Error('Failed to fetch data'); throw new Error('Failed to fetch data');
} }
} }
export async function getTask(id:number): Promise<ResponseVO<DataType>> {
noStore();
try {
// 使用 Axios 发送 GET 请求获取数据
const response: AxiosResponse<ResponseVO<DataType>> = await axios.get('http://localhost:8090/task/'+id);
// 从响应中提取数据并返回
return response.data;
} catch (error) {
// 处理错误
console.error('Error fetching data:', error);
// 返回一个默认值或者抛出错误
throw new Error('Failed to fetch data');
}
}
export async function addTask(task:DataType): Promise<ResponseVO<string>> {
noStore();
try {
// 使用 Axios 发送 POST 请求添加数据
const response: AxiosResponse<ResponseVO<string>> = await axios.post('http://localhost:8090/task', task);
// 从响应中提取数据并返回
return response.data;
} catch (error) {
// 处理错误
console.error('Error fetching data:', error);
// 返回一个默认值或者抛出错误
throw new Error('Failed to fetch data');
}
}
export async function updateTask(task:DataType): Promise<ResponseVO<string>> {
noStore();
try {
// 使用 Axios 发送 PUT 请求修改数据
const response: AxiosResponse<ResponseVO<string>> = await axios.put('http://localhost:8090/task', task);
// 从响应中提取数据并返回
return response.data;
} catch (error) {
// 处理错误
console.error('Error fetching data:', error);
// 返回一个默认值或者抛出错误
throw new Error('Failed to fetch data');
}
}
export async function deleteTask(id:number): Promise<ResponseVO<string>> {
noStore();
try {
// 使用 Axios 发送 DELETE 删除数据
const response: AxiosResponse<ResponseVO<string>> = await axios.delete('http://localhost:8090/task/'+id);
// 从响应中提取数据并返回
return response.data;
} catch (error) {
// 处理错误
console.error('Error fetching data:', error);
// 返回一个默认值或者抛出错误
throw new Error('Failed to fetch data');
}
}
export async function editState(id:number,state:number): Promise<ResponseVO<string>> {
noStore();
try {
// 使用 Axios 发送 DELETE 删除数据
const response: AxiosResponse<ResponseVO<string>> = await axios.patch('http://localhost:8090/task/'+id);
// 从响应中提取数据并返回
return response.data;
} catch (error) {
// 处理错误
console.error('Error fetching data:', error);
// 返回一个默认值或者抛出错误
throw new Error('Failed to fetch data');
}
}
export async function editPriority(id:number,priority:number): Promise<ResponseVO<string>> {
noStore();
try {
// 使用 Axios 发送 DELETE 删除数据
const response: AxiosResponse<ResponseVO<string>> = await axios.patch('http://localhost:8090/task/'+id);
// 从响应中提取数据并返回
return response.data;
} catch (error) {
// 处理错误
console.error('Error fetching data:', error);
// 返回一个默认值或者抛出错误
throw new Error('Failed to fetch data');
}
}
//0重要紧急红色1,重要不紧急黄色2不重要紧急灰色3不重要不紧急绿色
export const taskPriorityList: DictType[] = [
{
id: 0,
code: '0',
name: '重要紧急',
order: 0,
color: 'red'
}, {
id: 1,
code: '1',
name: '重要不紧急',
order: 1,
color: 'yellow'
}, {
id: 2,
code: '2',
name: '不重要紧急',
order: 2,
color: 'gary'
}, {
id: 3,
code: '3',
name: '不重要不紧急',
order: 3,
color: 'green'
}
]
export const TASK_PRIORITY_LIST: DictType[] = [
{
id: 0,
code: '0',
name: '重要紧急',
order: 0,
color: 'red'
}, {
id: 1,
code: '1',
name: '重要不紧急',
order: 1,
color: 'yellow'
}, {
id: 2,
code: '2',
name: '不重要紧急',
order: 2,
color: 'gary'
}, {
id: 3,
code: '3',
name: '不重要不紧急',
order: 3,
color: 'green'
}
]
// 0,暂存1,提交2审核3通过4拒绝5排期中6进行中7完成8bug修复9修复完成10确认11,上线运行
export const taskStateList: DictType[] = [
{
id: 0,
code: '0',
name: '暂存',
order: 1,
color: 'red'
}, {
id: 1,
code: '1',
name: '提交',
order: 1,
color: 'red'
}, {
id: 2,
code: '2',
name: '审核',
order: 2,
color: 'red'
}, {
id: 3,
code: '3',
name: '通过',
order: 3,
color: 'red'
}, {
id: 4,
code: '4',
name: '拒绝',
order: 4,
color: 'red'
}, {
id: 5,
code: '5',
name: '排期中',
order: 5,
color: 'red'
}, {
id: 6,
code: '6',
name: '排期中',
order: 6,
color: 'red'
}, {
id: 7,
code: '7',
name: '完成',
order: 7,
color: 'red'
}
]
export enum OPERATION_BUTTON_TYPE {
DETAIL,
ADD_CHILD,
UPDATE,
DELETE,
COMPLETE,
SHOW_FOUR
}

View File

@ -1,52 +1,57 @@
'use client'
import AcmeLogo from '@/app/ui/acme-logo'; import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline'; import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link'; import Link from 'next/link';
import Image from 'next/image'; import Image from 'next/image';
import {useRouter} from "next/navigation";
export default function Home() { export default function Home() {
const { replace } = useRouter();
replace("/task/project")
return ( return (
<main className="flex min-h-screen flex-col p-6"> <main className="flex min-h-screen flex-col p-6">
<div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52"> {/*<div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52">*/}
<AcmeLogo/> {/* <AcmeLogo/>*/}
</div> {/*</div>*/}
<div className="mt-4 flex grow flex-col gap-4 md:flex-row"> {/*<div className="mt-4 flex grow flex-col gap-4 md:flex-row">*/}
<div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20"> {/* <div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20">*/}
<p {/* <p*/}
className={`text-xl text-gray-800 md:text-3xl md:leading-normal`} {/* className={`text-xl text-gray-800 md:text-3xl md:leading-normal`}*/}
> {/* >*/}
<strong>Welcome to Acme.</strong> This is the example for the{' '} {/* <strong>Welcome to Acme.</strong> This is the example for the{' '}*/}
<a href="https://nextjs.org/learn/" className="text-blue-500"> {/* <a href="https://nextjs.org/learn/" className="text-blue-500">*/}
Next.js Learn Course {/* Next.js Learn Course*/}
</a> {/* </a>*/}
, brought to you by Vercel. {/* , brought to you by Vercel.*/}
</p> {/* </p>*/}
<Link {/* <Link*/}
href="/login" {/* href="/login"*/}
className="flex items-center gap-5 self-start rounded-lg bg-blue-500 px-6 py-3 text-sm font-medium text-white transition-colors hover:bg-blue-400 md:text-base" {/* className="flex items-center gap-5 self-start rounded-lg bg-blue-500 px-6 py-3 text-sm font-medium text-white transition-colors hover:bg-blue-400 md:text-base"*/}
> {/* >*/}
<span>Log in</span> {/* <span>Log in</span>*/}
{/*<ArrowRightIcon className="w-5 md:w-6"/>*/} {/* /!*<ArrowRightIcon className="w-5 md:w-6"/>*!/*/}
</Link> {/* </Link>*/}
</div> {/* </div>*/}
<div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12"> {/* <div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12">*/}
{/* Add Hero Images Here */} {/* /!* Add Hero Images Here *!/*/}
<Image {/* <Image*/}
src="/hero-desktop.png" {/* src="/hero-desktop.png"*/}
width={1000} {/* width={1000}*/}
height={760} {/* height={760}*/}
alt="Screenshots of the dashboard project showing desktop version" {/* alt="Screenshots of the dashboard project showing desktop version"*/}
className="hidden md:block" {/* className="hidden md:block"*/}
/> {/* />*/}
<Image {/* <Image*/}
src="/hero-mobile.png" {/* src="/hero-mobile.png"*/}
width={560} {/* width={560}*/}
height={620} {/* height={620}*/}
alt="Screenshot of the dashboard project showing mobile version" {/* alt="Screenshot of the dashboard project showing mobile version"*/}
className="block md:hidden" {/* className="block md:hidden"*/}
/> {/* />*/}
</div> {/* </div>*/}
</div> {/*</div>*/}
</main> </main>
); );
} }

31
src/app/task/error.tsx Normal file
View File

@ -0,0 +1,31 @@
'use client';
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Optionally log the error to an error reporting service
console.error(error);
}, [error]);
return (
<main className="flex h-full flex-col items-center justify-center">
<h2 className="text-center">Something went wrong!</h2>
<button
className="mt-4 rounded-md bg-blue-500 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-400"
onClick={
// Attempt to recover by trying to re-render the invoices route
() => reset()
}
>
Try again
</button>
</main>
);
}

View File

@ -0,0 +1,18 @@
import Link from 'next/link';
import { FaceFrownIcon } from '@heroicons/react/24/outline';
export default function NotFound() {
return (
<main className="flex h-full flex-col items-center justify-center gap-2">
<FaceFrownIcon className="w-10 text-gray-400" />
<h2 className="text-xl font-semibold">404 Not Found</h2>
<p>Could not find the requested invoice.</p>
<Link
href="/dashboard/invoices"
className="mt-4 rounded-md bg-blue-500 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-400"
>
Go Back
</Link>
</main>
);
}

View File

@ -1,12 +1,10 @@
import TreeTable from "@/app/ui/task/project/TreeTable"; import TreeTable from "@/app/ui/task/project/TreeTable";
import {DetailForm} from "@/app/ui/task/project/DetailForm";
const Page: React.FC = () => { const Page: React.FC = () => {
return ( return (
<> <>
<TreeTable/> <TreeTable/>
<DetailForm itemId={12} operationId={"1"}></DetailForm>
</> </>
); );
}; };

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import React, { useState } from 'react'; import React, {useEffect, useState} from 'react';
import { PlusOutlined } from '@ant-design/icons'; import {PlusOutlined} from '@ant-design/icons';
import { import {
Button, Button,
Cascader, Cascader,
@ -9,123 +9,312 @@ import {
DatePicker, DatePicker,
Form, Form,
Input, Input,
InputNumber, InputNumber, message,
Radio, Radio,
Select, Select,
Slider, Slider, Space,
Switch, Switch,
TreeSelect, TreeSelect,
Upload, Upload,
} from 'antd'; } from 'antd';
import {RangePickerProps} from "antd/es/date-picker";
import dayjs from "dayjs";
import {
addTask,
getTask,
OPERATION_BUTTON_TYPE,
taskPriorityList,
taskStateList,
updateTask
} from "@/app/lib/task/project/data";
import {DataType, DictType} from "@/app/lib/definitions";
export interface DetailFormProps { export interface DetailFormProps {
itemId: number, itemId: number,
operationId: string, pPid:number,
operationId: number|undefined,
handleCancel: () => void
} }
export const DetailForm: React.FC<DetailFormProps> = (props) => { export const DetailForm: React.FC<DetailFormProps> = (props) => {
const [form] = Form.useForm();
const [componentDisabled, setComponentDisabled] =
const [componentDisabled, setComponentDisabled] = useState<boolean>(props.operationId === OPERATION_BUTTON_TYPE.DETAIL);
useState<boolean>(props.operationId==='1'); const {RangePicker} = DatePicker;
const {TextArea} = Input;
const { RangePicker } = DatePicker; // const [taskMessage,setTaskMessage]=useState<any>({name:"useState没效果吗,是这样的"});
const { TextArea } = Input; let taskMessage;
useEffect(() => {
const normFile = (e: any) => { if (props.operationId === OPERATION_BUTTON_TYPE.DETAIL||props.operationId === OPERATION_BUTTON_TYPE.UPDATE) {
if (Array.isArray(e)) { getTask(props.itemId).then(task => {
return e; console.log('getTask(props.itemId)',props.itemId,task);
} if(task.status.success){
return e?.fileList; // setTaskMessage(task.data)
task.data.state=taskStateList.find(taskState=>taskState.code===task.data.state?.toString())?.name;
task.data.priority=taskPriorityList.find(taskPriority=>taskPriority.code===task.data.priority?.toString())?.name;
form.setFieldsValue(task.data)
}else{
message.error(task.status.message);
props.handleCancel()
}
})
}
},[])
const normFile = (e: any) => {
if (Array.isArray(e)) {
return e;
}
return e?.fileList;
};
const range = (start: number, end: number) => {
const result = [];
for (let i = start; i < end; i++) {
result.push(i);
}
return result;
};
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
// return current && current < dayjs().endOf('day');
console.log('current', current, current && current < dayjs().endOf('day'), current < dayjs().endOf('day'))
return current < dayjs().startOf('day');
};
const disabledRangeTime: RangePickerProps['disabledTime'] = (_, type) => {
console.log('current', _)
if (type === 'start') {
return {
disabledHours: () => range(0, 60).splice(4, 20),
disabledMinutes: () => range(30, 60),
disabledSeconds: () => [55, 56],
};
}
return {
disabledHours: () => range(0, 60).splice(20, 4),
disabledMinutes: () => range(0, 31),
disabledSeconds: () => [55, 56],
}; };
return ( };
<> const onFinish = (values: any) => {
<Checkbox console.log(values);
checked={componentDisabled} let request: DataType = {
onChange={(e) => setComponentDisabled(e.target.checked)} key: values.id,
> id: values.id,
Form disabled pPid: props.operationId===OPERATION_BUTTON_TYPE.ADD_CHILD?props.pPid:values.pPid,
</Checkbox> pid: props.operationId===OPERATION_BUTTON_TYPE.ADD_CHILD?props.itemId:props.operationId===OPERATION_BUTTON_TYPE.UPDATE?values.pid:0,
<Form code: values.code,
labelCol={{span: 4}} name: values.name,
wrapperCol={{span: 14}} description: values.description,
layout="horizontal" state: values.state,
disabled={componentDisabled} priority: values.priority,
style={{maxWidth: 600}} type: values.type,
> children: []
<Form.Item label="Checkbox" name="disabled" valuePropName="checked"> }
<Checkbox>Checkbox</Checkbox> if (values.expectedStartTime) {
</Form.Item> if (values.expectedStartTime[0]) {
<Form.Item label="Radio"> request.expectedStartTime = values.expectedStartTime[0]
<Radio.Group> }
<Radio value="apple"> Apple </Radio> if (values.expectedStartTime[1]) {
<Radio value="pear"> Pear </Radio> request.expectedEndTime = values.expectedStartTime[1]
</Radio.Group> }
</Form.Item> }
<Form.Item label="Input"> if (values.actualStartTime) {
<Input/> if (values.actualStartTime[0]) {
</Form.Item> request.actualStartTime = values.actualStartTime[0]
<Form.Item label="Select"> }
<Select> if (values.actualStartTime[1]) {
<Select.Option value="demo">Demo</Select.Option> request.actualEndTime = values.actualStartTime[1]
</Select> }
</Form.Item> }
<Form.Item label="TreeSelect"> if (props.operationId===OPERATION_BUTTON_TYPE.ADD_CHILD){
<TreeSelect addTask(request).then(response => {
treeData={[ console.log('response',response)
{title: 'Light', value: 'light', children: [{title: 'Bamboo', value: 'bamboo'}]}, if (response.status.success) {
]} message.success("添加任务成功:"+response.data)
/> props.handleCancel()
</Form.Item> }
<Form.Item label="Cascader"> }
<Cascader )
options={[ }else if(props.operationId===OPERATION_BUTTON_TYPE.UPDATE){
{ var stateFind = taskStateList.find(taskState=>taskState.name===request.state?.toString());
value: 'zhejiang', if (stateFind){
label: 'Zhejiang', request.state=stateFind.code;
children: [ }
{ var priorityFind =taskPriorityList.find(taskPriority=>taskPriority.name===request.priority?.toString());
value: 'hangzhou', if(priorityFind){
label: 'Hangzhou', request.priority=priorityFind.code;
}, }
], updateTask(request).then(response => {
}, console.log('response',response)
]} if (response.status.success) {
/> message.success("修改任务成功:"+response.data)
</Form.Item> props.handleCancel()
<Form.Item label="DatePicker"> }
<DatePicker/> }
</Form.Item> )
<Form.Item label="RangePicker"> }
<RangePicker/>
</Form.Item>
<Form.Item label="InputNumber"> };
<InputNumber/> return (
</Form.Item> <>
<Form.Item label="TextArea"> {/*<Checkbox*/}
<TextArea rows={4}/> {/* checked={componentDisabled}*/}
</Form.Item> {/* onChange={(e) => setComponentDisabled(e.target.checked)}*/}
<Form.Item label="Switch" valuePropName="checked"> {/*>*/}
<Switch/> {/* Form disabled*/}
</Form.Item> {/*</Checkbox>*/}
<Form.Item label="Upload" valuePropName="fileList" getValueFromEvent={normFile}> <Form
<Upload action="/upload.do" listType="picture-card"> labelCol={{span: 4}}
<button style={{border: 0, background: 'none'}} type="button"> wrapperCol={{span: 14}}
<PlusOutlined/> layout="horizontal"
<div style={{marginTop: 8}}>Upload</div> disabled={componentDisabled}
</button> size='large'
</Upload> onFinish={onFinish}
</Form.Item> initialValues={taskMessage}
<Form.Item label="Button"> form={form}
<Button>Button</Button> // style={{maxWidth: '70%'}}
</Form.Item> >
<Form.Item label="Slider"> <Form.Item<DataType> name='id' label="id" hidden={true}>
<Slider/> <Input name='id'/>
</Form.Item> </Form.Item>
<Form.Item label="ColorPicker"> <Form.Item<DataType> name='pid' label="pid" hidden={true}>
<ColorPicker/> <Input name='pid'/>
</Form.Item> </Form.Item>
</Form> <Form.Item<DataType> name='code' label="code" hidden={true}>
</> <Input name='code'/>
); </Form.Item>
<Form.Item<DataType> name='pPid' label="pPid" hidden={true}>
<Input name='pPid'/>
</Form.Item>
<Form.Item<DataType> name='name' label="任务名称" rules={[{ required: true, message: '任务名称不能为空' }]}>
<Input name='name'/>
</Form.Item>
<Form.Item<DataType> name='description' label="任务描述">
<TextArea name='description' rows={4}/>
</Form.Item>
<Form.Item<DataType> name='priority' label="任务优先级">
<Select options={
taskPriorityList.map(taskState => {
return {
'label': taskState.name,
'value': taskState.code
}
})
}>
</Select>
</Form.Item>
<Form.Item<DataType> name='state' label="任务状态">
<Select options={
taskStateList.map(taskState => {
return {
'label': taskState.name,
'value': taskState.code
}
})
}>
</Select>
</Form.Item>
<Form.Item<DataType> name={['expectedStartTime']} label="期望">
<RangePicker
disabledDate={disabledDate}
disabledTime={disabledRangeTime}
placeholder={['开始时间', '结束时间']}
allowEmpty={[true, true]}
showTime={{
hideDisabledOptions: true,
defaultValue: [dayjs('00:00:00', 'HH:mm:ss'), dayjs('11:59:59', 'HH:mm:ss')],
}}
// format="YYYY-MM-DD HH:mm:ss"
/>
</Form.Item>
<Form.Item<DataType> name='actualStartTime' label="实际">
<RangePicker
disabledDate={disabledDate}
disabledTime={disabledRangeTime}
placeholder={['开始时间', '结束时间']}
allowEmpty={[true, true]}
showTime={{
hideDisabledOptions: true,
defaultValue: [dayjs('00:00:00', 'HH:mm:ss'), dayjs('11:59:59', 'HH:mm:ss')],
}}
// format="YYYY-MM-DD HH:mm:ss"
/>
</Form.Item>
{/*<Form.Item label="InputNumber">*/}
{/* <InputNumber/>*/}
{/*</Form.Item>*/}
{/*<Form.Item label="Checkbox" name="disabled" valuePropName="checked">*/}
{/* <Checkbox>Checkbox</Checkbox>*/}
{/*</Form.Item>*/}
{/*<Form.Item label="Radio">*/}
{/* <Radio.Group>*/}
{/* <Radio value="apple"> Apple </Radio>*/}
{/* <Radio value="pear"> Pear </Radio>*/}
{/* </Radio.Group>*/}
{/*</Form.Item>*/}
{/*<Form.Item label="TreeSelect">*/}
{/* <TreeSelect*/}
{/* treeData={[*/}
{/* {title: 'Light', value: 'light', children: [{title: 'Bamboo', value: 'bamboo'}]},*/}
{/* ]}*/}
{/* />*/}
{/*</Form.Item>*/}
{/*<Form.Item label="Cascader">*/}
{/* <Cascader*/}
{/* options={[*/}
{/* {*/}
{/* value: 'zhejiang',*/}
{/* label: 'Zhejiang',*/}
{/* children: [*/}
{/* {*/}
{/* value: 'hangzhou',*/}
{/* label: 'Hangzhou',*/}
{/* },*/}
{/* ],*/}
{/* },*/}
{/* ]}*/}
{/* />*/}
{/*</Form.Item>*/}
{/*<Form.Item label="DateTimePicker">*/}
{/* <DatePicker/>*/}
{/*</Form.Item>*/}
{/*<Form.Item label="Switch" valuePropName="checked">*/}
{/* <Switch/>*/}
{/*</Form.Item>*/}
{/*<Form.Item label="Upload" valuePropName="fileList" getValueFromEvent={normFile}>*/}
{/* <Upload action="/upload.do" listType="picture-card">*/}
{/* <button style={{border: 0, background: 'none'}} type="button">*/}
{/* <PlusOutlined/>*/}
{/* <div style={{marginTop: 8}}>Upload</div>*/}
{/* </button>*/}
{/* </Upload>*/}
{/*</Form.Item>*/}
{/*<Form.Item label="Button">*/}
{/* <Button>Button</Button>*/}
{/*</Form.Item>*/}
{/*<Form.Item label="Slider">*/}
{/* <Slider/>*/}
{/*</Form.Item>*/}
{/*<Form.Item label="ColorPicker">*/}
{/* <ColorPicker/>*/}
{/*</Form.Item>*/}
<Form.Item>
<Space>
{props.operationId === OPERATION_BUTTON_TYPE.DETAIL ? (
// <Button htmlType="button">关闭</Button>
<>
</>
) : (<>
<Button type="primary" htmlType="submit"></Button>
<Button htmlType="button"></Button>
</>
)
}
</Space>
</Form.Item>
</Form>
</>
);
} }

View File

@ -1,71 +1,101 @@
import React, {Fragment} from "react"; import React, {Fragment} from "react";
import {Button, Dropdown, MenuProps, Modal, Popconfirm, Space} from "antd"; import {Button, Dropdown, MenuProps, message, Modal, Popconfirm, Space} from "antd";
import {DownOutlined, QuestionCircleOutlined} from "@ant-design/icons"; import {DownOutlined, QuestionCircleOutlined} from "@ant-design/icons";
import {DetailForm} from "@/app/ui/task/project/DetailForm"; import {DetailForm} from "@/app/ui/task/project/DetailForm";
import {deleteTask, OPERATION_BUTTON_TYPE} from "@/app/lib/task/project/data";
export interface OperationButtonProps { export interface OperationButtonProps {
itemId: number itemId: number,
pid: number,
pPid: number,
operationId?: string,
refreshDate?: () => void
} }
interface OperationModelProps { interface OperationModelProps {
operationId: string, operationId: number | undefined,
openModal:boolean pPid:number,
pid: number,
openModal: boolean
} }
class OperationButton extends React.Component<OperationButtonProps,OperationModelProps> {
class OperationButton extends React.Component<OperationButtonProps, OperationModelProps> {
constructor(props: OperationButtonProps) { constructor(props: OperationButtonProps) {
super(props); super(props);
this.state = { this.state = {
operationId: '', pid: props.pid,
openModal:false pPid: props.pPid,
operationId: undefined,
openModal: false
}; };
} }
render() { render() {
const handleCancel =()=>{ const handleCancel = () => {
this.setState({...this.state,openModal:false}) this.setState({...this.state, openModal: false})
if (this.state.operationId !== OPERATION_BUTTON_TYPE.DETAIL) {
this.props.refreshDate?.()
}
} }
const onClick: MenuProps['onClick'] = ({key}) => {
console.log(key)
};
const items: MenuProps['items'] = [ const items: MenuProps['items'] = [
{ {
key: '1', key: OPERATION_BUTTON_TYPE.DETAIL,
label: <a onClick={(e) => { label: <a onClick={(e) => {
this.setState({...this.state,openModal:true}) this.setState({openModal: true, operationId: OPERATION_BUTTON_TYPE.DETAIL})
}}></a>, }}></a>,
}, },
{ {
key: '2', key: OPERATION_BUTTON_TYPE.ADD_CHILD,
label: <a onClick={(e) => { label: <a onClick={(e) => {
this.setState({...this.state,openModal:true}) this.setState({openModal: true, operationId: OPERATION_BUTTON_TYPE.ADD_CHILD})
}}></a>, }}>线</a>,
}, },
{ {
key: '3', key: OPERATION_BUTTON_TYPE.UPDATE,
label: <a onClick={(e) => { label: <a onClick={(e) => {
this.setState({...this.state,openModal:true}) this.setState({openModal: true, operationId: OPERATION_BUTTON_TYPE.UPDATE})
}}></a>, }}></a>,
} }
, ,
{ {
key: '4', key: OPERATION_BUTTON_TYPE.DELETE,
label: <Popconfirm label: <Popconfirm
title="删除任务" title="删除任务"
description="确认要删除任务?" description="确认要删除任务?"
icon={<QuestionCircleOutlined style={{color: 'red'}}/>} icon={<QuestionCircleOutlined style={{color: 'red'}}/>}
okText="确认" okText="确认"
cancelText="取消" cancelText="取消"
></Popconfirm>, onConfirm={() => {
} deleteTask(this.props.itemId).then((response => {
, console.log('response', response)
if (response.status.success) {
message.success("删除任务成功:" + response.data)
this.props.refreshDate?.()
}
}));
}}
><a></a></Popconfirm>,
},
{ {
key: '5', key: OPERATION_BUTTON_TYPE.COMPLETE,
label: <Popconfirm label: <Popconfirm
title="完成任务" title="完成任务"
description="确认要完成任务?" description="确认要完成任务?"
okText="确认" okText="确认"
cancelText="取消" cancelText="取消"
></Popconfirm>, ><a></a></Popconfirm>,
},
{
key: OPERATION_BUTTON_TYPE.SHOW_FOUR,
label: <a></a>,
} }
]; ];
return <Fragment> return <Fragment>
<Dropdown menu={{items}}> <Dropdown menu={{items, onClick}}>
<a onClick={(e) => { <a onClick={(e) => {
e.preventDefault() e.preventDefault()
}}> }}>
@ -75,11 +105,17 @@ class OperationButton extends React.Component<OperationButtonProps,OperationMode
</a> </a>
</Dropdown> </Dropdown>
<Modal <Modal
maskClosable={false}
destroyOnClose={true}
open={this.state.openModal} open={this.state.openModal}
title="Title" title={this.state.operationId === OPERATION_BUTTON_TYPE.DETAIL ? '任务详情' :
this.state.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD ? '添加支线任务' :
this.state.operationId === OPERATION_BUTTON_TYPE.UPDATE ? '修改任务' : '未知操作'}
// open={open} // open={open}
// onOk={handleOk} // onOk={handleOk}
onCancel={handleCancel} onCancel={handleCancel}
footer={[]}
width={800}
// footer={[ // footer={[
// <Button key="back" onClick={handleCancel}> // <Button key="back" onClick={handleCancel}>
// Return // Return
@ -98,7 +134,11 @@ class OperationButton extends React.Component<OperationButtonProps,OperationMode
// </Button>, // </Button>,
// ]} // ]}
> >
<DetailForm itemId={this.props.itemId} operationId={'1'}/> <DetailForm itemId={this.props.itemId}
operationId={this.state.operationId}
handleCancel={handleCancel}
pPid={this.props.pPid}
/>
</Modal> </Modal>
</Fragment> </Fragment>
} }

View File

@ -1,13 +0,0 @@

View File

@ -2,7 +2,7 @@
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import {Button, ColorPicker, Dropdown, MenuProps, Space, Switch, Table} from 'antd'; import {Button, ColorPicker, Dropdown, MenuProps, Space, Switch, Table} from 'antd';
import type { TableColumnsType, TableProps } from 'antd'; import type { TableColumnsType, TableProps } from 'antd';
import { taskTreeResult} from "@/app/lib/task/project/data"; import {taskPriorityList, taskStateList,TASK_PRIORITY_LIST, taskTreeResult} from "@/app/lib/task/project/data";
import {DataType, ResponseVO, ResultPage} from "@/app/lib/definitions"; import {DataType, ResponseVO, ResultPage} from "@/app/lib/definitions";
import {DownOutlined} from "@ant-design/icons"; import {DownOutlined} from "@ant-design/icons";
import OperationButton from "@/app/ui/task/project/OperationButton"; import OperationButton from "@/app/ui/task/project/OperationButton";
@ -63,29 +63,40 @@ const rowSelection: TableRowSelection<DataType> = {
console.log(selected, selectedRows, changeRows); console.log(selected, selectedRows, changeRows);
}, },
}; };
function recursionActionChild(children:DataType[]){
if (children.length===0){
return;
}
children.forEach(item=>{
item.key=item.id;
item.action=(<OperationButton itemId={item.id}></OperationButton>)
recursionActionChild(item.children)
})
}
const TreeTable: React.FC = () => { const TreeTable: React.FC = () => {
console.log("TreeTable: React.FC ",TASK_PRIORITY_LIST,taskPriorityList)
function recursionActionChild(children:DataType[]){
if (children.length===0){
return;
}
console.log("TreeTable: React.FC :recursionActionChild",TASK_PRIORITY_LIST,taskPriorityList)
for (let item of children) {
item.key=item.id;
item.action=(<OperationButton itemId={item.id} pid={item.pid} pPid={item.pPid} refreshDate={refreshDate}/>)
item.state=taskStateList.find(taskState=>taskState.code===item.state?.toString())?.name;
item.priority=taskPriorityList.find(taskPriority=>taskPriority.code===item.priority?.toString())?.name;
if (item.children&&item.children.length>0){
recursionActionChild(item.children)
}else {
item.children=undefined
}
}
}
// const [checkStrictly, setCheckStrictly] = useState(false); // const [checkStrictly, setCheckStrictly] = useState(false);
const [data, setData] = useState<DataType[]>([]); const [data, setData] = useState<DataType[]>([]);
const [pageNumber, setPageNumber] = useState<number>(1); const [pageNumber, setPageNumber] = useState<number>(1);
const [pageSize, setPageSize] = useState<number>(10); const [pageSize, setPageSize] = useState<number>(10);
useEffect(() => { const refreshDate=():void=>{
console.log("useEffect::taskTreeResult")
taskTreeResult().then((result:ResponseVO<ResultPage<DataType>>)=>{ taskTreeResult().then((result:ResponseVO<ResultPage<DataType>>)=>{
if (result.status.success){ if (result.status.success){
recursionActionChild(result.data.content); recursionActionChild(result.data.content);
setData(result.data.content) setData(result.data.content)
} }
}) })
}
useEffect(() => {
refreshDate();
}, []); }, []);
return ( return (
<> <>
@ -93,6 +104,8 @@ const TreeTable: React.FC = () => {
{/* CheckStrictly: <Switch checked={checkStrictly} onChange={setCheckStrictly} />*/} {/* CheckStrictly: <Switch checked={checkStrictly} onChange={setCheckStrictly} />*/}
{/*</Space>*/} {/*</Space>*/}
<Button type="primary">线</Button> <Button type="primary">线</Button>
<Button type="primary"></Button>
<Button type="primary"></Button>
<ColorPicker defaultValue="#1677ff" showText/> <ColorPicker defaultValue="#1677ff" showText/>
<Table <Table
columns={columns} columns={columns}