feat:1.主线计划选择,多提醒及耗时修改

This commit is contained in:
1708-huayu 2025-08-08 18:55:41 +08:00
parent 09d4357719
commit c6444b2662
6 changed files with 206 additions and 4430 deletions

View File

@ -1,3 +1,7 @@
.localDiv{
margin-bottom: 24px;
}
.icon{
font-size: 1rem;
font-color: orange;
}

View File

@ -1,7 +1,9 @@
import styles from "@/components/TaskRemind.module.css";
import {CascaderProps, ConfigProvider} from 'antd';
import {CascaderProps, ConfigProvider, message} from 'antd';
import {Cascader} from 'antd';
import React, {useState} from "react";
import React, {Fragment, useState} from "react";
import {DeleteOutlined, PlusOutlined} from "@ant-design/icons";
import {CascaderOption} from "@/lib/definitions";
interface ITaskRemind {
remindTypeList: string[],
@ -9,14 +11,7 @@ interface ITaskRemind {
readonly: boolean,
}
interface Option {
value?: string | number | null;
label: React.ReactNode;
children?: Option[];
isLeaf?: boolean;
}
const optionLists: Option[] = [
const optionLists: CascaderOption[] = [
{
label: "期望开始",
value: "expect_start",
@ -30,11 +25,54 @@ const optionLists: Option[] = [
];
const TaskRemindComponent = (props: ITaskRemind) => {
const [options, setOptions] = useState<Option[]>(optionLists);
const cascaderOnChange: CascaderProps<Option>['onChange'] = (value: (string | number)[], selectedOptions: Option[]) => {
console.log(value, selectedOptions);
const [options, setOptions] = useState<CascaderOption[]>(optionLists);
// : CascaderProps<Option>['onChange']
const cascaderOnChange = (value: (string | number)[], selectedOptions: CascaderOption[], index: number) => {
console.log(value, selectedOptions, index);
if(value){
props.remindTypeList[index] = value.join(",")
props.setRemindTypeList([...props.remindTypeList])
}
};
const cascaderLoadData = (selectedOptions: Option[]) => {
const removeRemindType = (index: number) => {
props.remindTypeList.splice(index, 1)
props.setRemindTypeList([...props.remindTypeList])
}
const addRemindType = () => {
if (props.remindTypeList.length>3){
message.error("提醒次数超额")
}else{
props.remindTypeList.push("")
props.setRemindTypeList([...props.remindTypeList])
}
}
function changeValueToLabel(taskRemind: string) {
console.log({taskRemind})
if (!taskRemind) {
return []
}
let taskRemindArray: string[] = taskRemind.split(",");
if (taskRemindArray.length != 4) {
return []
}
if (taskRemindArray[1] == "before") {
taskRemindArray[1] = "前"
} else if (taskRemindArray[1] == "after") {
taskRemindArray[1] = "后"
}
if (taskRemindArray[3] == "m") {
taskRemindArray[3] = "分钟"
} else if (taskRemindArray[3] == "h") {
taskRemindArray[3] = "小时"
} else if (taskRemindArray[3] == "d") {
taskRemindArray[3] = "天"
}
return taskRemindArray;
}
const cascaderLoadData = (selectedOptions: CascaderOption[]) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
if (targetOption.value === "expect_start" || targetOption.value === "expect_end") {
targetOption.children = [
@ -51,7 +89,8 @@ const TaskRemindComponent = (props: ITaskRemind) => {
];
} else if (targetOption.value === "before" || targetOption.value === "after") {
targetOption.children = Array.from({length: 60}, (_, i) => ({
label: i.toString().padStart(2, '0'), // 显示为 "00", "01", ..., "59"
// label: i.toString().padStart(2, '0'), // 显示为 "00", "01", ..., "59"
label: i,
value: i,
isLeaf: false,
}))
@ -65,12 +104,12 @@ const TaskRemindComponent = (props: ITaskRemind) => {
{
label: "小时",
value: "h",
isLeaf: false,
isLeaf: true,
},
{
label: "天",
value: "d",
isLeaf: false,
isLeaf: true,
}
]
}
@ -143,7 +182,7 @@ const TaskRemindComponent = (props: ITaskRemind) => {
<div>-</div>
) : (
props.remindTypeList.map((remindType, index) => (
<div key={index}>{remindType.split(",").join(", ")}</div>
<div key={index}>{changeValueToLabel(remindType)}</div>
))
)
) : props.remindTypeList.length === 0 ? (
@ -152,16 +191,42 @@ const TaskRemindComponent = (props: ITaskRemind) => {
components: {
Cascader: {
/* 这里是你的组件 token */
controlWidth:300
controlWidth: 300
},
},
}}
>
<Cascader options={options} loadData={cascaderLoadData} onChange={cascaderOnChange} changeOnSelect defaultValue={["expect_start","after","1","h"]} />
<Cascader options={options} loadData={cascaderLoadData}
onChange={(e, o) => cascaderOnChange(e, o, 0)}
clearIcon={false}
// 允许只选择父级
// changeOnSelect
/>
</ConfigProvider>
) : (
props.remindTypeList.map((remindType, index) => (
<div key={index}>{remindType.split(",").join(", ")}</div>
// <div key={index}>{remindType.split(",").join(", ")}</div>
<Fragment>
<ConfigProvider
theme={{
components: {
Cascader: {
/* 这里是你的组件 token */
controlWidth: 300
},
},
}}
>
<Cascader options={options} key={index} loadData={cascaderLoadData}
onChange={(e, o) => cascaderOnChange(e, o, index)}
defaultValue={changeValueToLabel(remindType)}
clearIcon={false}
// value={remindType.split(",")}
/>
</ConfigProvider>
<DeleteOutlined className="icon" onClick={() => removeRemindType(index)}/>
{index == props.remindTypeList.length - 1 && props.remindTypeList.length<3 && <PlusOutlined className="icon" onClick={addRemindType}/>}
</Fragment>
))
)}
</div>)

View File

@ -1,242 +0,0 @@
[
{
"label": "0",
"value": "0"
},
{
"label": "1",
"value": "1"
},
{
"label": "2",
"value": "2"
},
{
"label": "3",
"value": "3"
},
{
"label": "4",
"value": "4"
},
{
"label": "5",
"value": "5"
},
{
"label": "6",
"value": "6"
},
{
"label": "7",
"value": "7"
},
{
"label": "8",
"value": "8"
},
{
"label": "9",
"value": "9"
},
{
"label": "10",
"value": "10"
},
{
"label": "11",
"value": "11"
},
{
"label": "12",
"value": "12"
},
{
"label": "13",
"value": "13"
},
{
"label": "14",
"value": "14"
},
{
"label": "15",
"value": "15"
},
{
"label": "16",
"value": "16"
},
{
"label": "17",
"value": "17"
},
{
"label": "18",
"value": "18"
},
{
"label": "19",
"value": "19"
},
{
"label": "20",
"value": "20"
},
{
"label": "21",
"value": "21"
},
{
"label": "22",
"value": "22"
},
{
"label": "23",
"value": "23"
},
{
"label": "24",
"value": "24"
},
{
"label": "25",
"value": "25"
},
{
"label": "26",
"value": "26"
},
{
"label": "27",
"value": "27"
},
{
"label": "28",
"value": "28"
},
{
"label": "29",
"value": "29"
},
{
"label": "30",
"value": "30"
},
{
"label": "31",
"value": "31"
},
{
"label": "32",
"value": "32"
},
{
"label": "33",
"value": "33"
},
{
"label": "34",
"value": "34"
},
{
"label": "35",
"value": "35"
},
{
"label": "36",
"value": "36"
},
{
"label": "37",
"value": "37"
},
{
"label": "38",
"value": "38"
},
{
"label": "39",
"value": "39"
},
{
"label": "40",
"value": "40"
},
{
"label": "41",
"value": "41"
},
{
"label": "42",
"value": "42"
},
{
"label": "43",
"value": "43"
},
{
"label": "44",
"value": "44"
},
{
"label": "45",
"value": "45"
},
{
"label": "46",
"value": "46"
},
{
"label": "47",
"value": "47"
},
{
"label": "48",
"value": "48"
},
{
"label": "49",
"value": "49"
},
{
"label": "50",
"value": "50"
},
{
"label": "51",
"value": "51"
},
{
"label": "52",
"value": "52"
},
{
"label": "53",
"value": "53"
},
{
"label": "54",
"value": "54"
},
{
"label": "55",
"value": "55"
},
{
"label": "56",
"value": "56"
},
{
"label": "57",
"value": "57"
},
{
"label": "58",
"value": "58"
},
{
"label": "59",
"value": "59"
}
]

File diff suppressed because it is too large Load Diff

View File

@ -71,3 +71,10 @@ export type SearchObject={
value: any,
operateType:string,
}
export interface CascaderOption {
value?: string | number | null;
label: React.ReactNode;
children?: CascaderOption[];
isLeaf?: boolean;
}

View File

@ -6,7 +6,7 @@ import {
ProFormSelect,
ProFormText, ProFormTextArea, ProFormTreeSelect,
} from '@ant-design/pro-components';
import {Button, Form, message, Popconfirm, Select, Space, Spin} from 'antd';
import {Button, Cascader, CascaderProps, Form, message, Popconfirm, Select, Space, Spin} from 'antd';
import React, {Fragment, useEffect, useState} from "react";
import {
addTask, deleteTask, getTask,
@ -15,14 +15,13 @@ import {
taskPriorityList,
taskStateList, updateTask
} from "@/lib/task/project/data";
import {DataType} from "@/lib/definitions";
import {CascaderOption, DataType} from "@/lib/definitions";
import dayjs, {Dayjs} from "dayjs";
import DiaryOption from "@/components/DiaryOption";
import ShareOption from "@/components/ShareOption";
import StepSort from "@/components/StepSort";
import SettingCron from "@/components/SettingCron";
import ClickRecord from "@/components/ClickRecord";
import onceConsumeList from "@/components/constant/onceConsumeList.json"
import {onceConsumerRead} from "@/utils/codeToReadName";
import style from "@/ui/task/project/DetailModelForm.module.css"
import TeamMember from "@/components/TeamMember";
@ -78,12 +77,9 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
const [spinning, setSpinning] = useState(true)
const [operationRequest, setOperationRequest] = useState(false)
const [onceConsumeChange, setOnceConsumeChange] = useState<string[]>(["1", "h"])
const [defaultPTask, setDefaultPTask] = useState<ParentTaskVO>({
pid: '0',
pName: undefined,
fId: undefined,
fName: undefined,
})
const [defaultPTask, setDefaultPTask] = useState<ParentTaskVO>()
const [selectPid,setSelectPid]=useState<string>();
// key为当前任务idvalue为当前任务作为父任务信息
const [pTaskMap, setPTaskMap] = useState<Record<string, ParentTaskVO>>()
const [remindTypeList,setRemindTypeList] = useState<string[]>([])
@ -99,6 +95,54 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
fName: undefined,
})
}
const [options, setOptions] = useState<CascaderOption[]>( Array.from({length: 60}, (_, i) => ({
// label: i.toString().padStart(2, '0'), // 显示为 "00", "01", ..., "59"
label: i,
value: i,
isLeaf: false,
})));
const cascaderLoadData = (selectedOptions: CascaderOption[]) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.children = [
{
label: "分钟",
value: "m",
isLeaf: true,
},
{
label: "小时",
value: "h",
isLeaf: true,
},
{
label: "天",
value: "d",
isLeaf: true,
}
]
setOptions([...options])
}
const cascaderOnChange: CascaderProps<CascaderOption>['onChange'] =
(value: (string | number)[], selectedOptions: CascaderOption[]) => {
setOnceConsumeChange(value.map(toString))
};
const changeValueToLabel = ()=>{
if (onceConsumeChange&&onceConsumeChange.length==2){
if(onceConsumeChange[1]=='m'){
onceConsumeChange[1]='分钟'
}else if(onceConsumeChange[1]=='h'){
onceConsumeChange[1]='小时'
}else if(onceConsumeChange[1]=='d'){
onceConsumeChange[1]='天'
}
return onceConsumeChange;
}else {
return []
}
}
useEffect(() => {
if (searchParams && searchParams.get("pid")) {
setDefaultPTask({
@ -107,6 +151,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
fId: searchParams.get("fId")!,
fName: searchParams.get("fName")!,
})
setSelectPid(searchParams.get("pid")!)
} else {
setDefaultPTask({
pid: '0',
@ -114,6 +159,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
fId: undefined,
fName: undefined,
})
setSelectPid('0')
}
}, [searchParams]);
@ -179,7 +225,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
label: data.name, value: data.id, pid: data.pid,
fId: data.fId, fName: data.fName
};
parentTaskMap[data.id] = {pid: data.pid, pName: data.pName, fId: data.fId, fName: data.fName};
parentTaskMap[data.id] = {pid: data.id, pName: data.name, fId: data.fId, fName: data.fName};
if (data.children) {
resultData.children = childReduce(data.children);
}
@ -309,9 +355,9 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
const {sortNo} = requestTask;
values.sortNo = sortNo;
}
values.pid = defaultPTask.pid;
values.fId = defaultPTask.fId;
values.fName = defaultPTask.fName;
values.pid = defaultPTask!.pid;
values.fId = defaultPTask!.fId;
values.fName = defaultPTask!.fName;
if (values.pid === undefined) {
values.pid = '0'
}
@ -329,7 +375,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
}
var result: boolean = false;
values.remindType=remindTypeList;
values.onceConsume=onceConsumeChange.join(',')
let state = taskStateList.find(taskState => taskState.name === values.state?.toString());
if (state) {
values.state = state.code
@ -351,7 +397,6 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
props.reloadData?.()
result = true
} else {
message.error(response.data.status.message)
result = false
}
}
@ -372,7 +417,6 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
}
props.reloadData?.()
} else {
message.error(response.data.status.message)
result = false
}
}
@ -425,15 +469,17 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
disabled={editFormDisable}
/>
<ProFormTreeSelect
params={{pid:selectPid}}
hidden={taskType == '1'}
width="sm"
initialValue={defaultPTask.pName}
request={() => {
initialValue={defaultPTask?.pName}
request={(params, props) => {
console.log("request",{params},{props})
return getTaskTreeResult(JSON.stringify(
{
pageSize: 1000,
pageNumber: 1,
data: [{code: 'pid', value: defaultPTask.pid, operateType: '='},
data: [{code: 'pid', value: params.pid, operateType: '='},
// 如果父任务完成会导致父任务不展示
// {
// code: 'state',
@ -447,8 +493,14 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
name="pid"
label="父级任务"
fieldProps={{
showSearch:true,
treeNodeFilterProp:'label',
// filterTreeNode: (inputValue, treeNode) => {
// // 根据 label 进行过滤
// return treeNode.label!.toString().toLowerCase().includes(inputValue.toLowerCase());
// },
onSelect: (e, node) => {
console.log('onSelect', e, node);
console.log('onSelect', e, node,pTaskMap);
setDefaultPTask(pTaskMap?.[e] || {
pid: '0',
pName: undefined,
@ -463,6 +515,9 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
fId: undefined,
fName: undefined,
})
if(selectPid!='0') {
setSelectPid("0")
}
}
}}
disabled={editFormDisable}
@ -567,32 +622,40 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
<text>:{onceConsumerRead(requestTask?.onceConsume)}</text>
</div> :
<div className={style.localDiv}>
<Space wrap>
<text>:</text>
<Select
style={{width: 120}}
allowClear
defaultValue={onceConsumeChange[0]}
onChange={(value) => {
onceConsumeChange[0] = value
setOnceConsumeChange(onceConsumeChange)
}}
options={onceConsumeList}
/>
<Select
style={{width: 120}}
allowClear
defaultValue={onceConsumeChange[1]}
onChange={(value) => {
onceConsumeChange[1] = value
setOnceConsumeChange(onceConsumeChange)
}}
options={[{label: "分钟", value: "m"}, {label: "小时", value: "h"}, {
label: "天",
value: "d"
}]}
/>
</Space>
<text style={{paddingRight:'8px'}}>:</text>
<Cascader options={options} loadData={cascaderLoadData}
onChange={cascaderOnChange}
defaultValue={changeValueToLabel()}
onClick={()=>setOnceConsumeChange([])}
clearIcon={true}
// value={remindType.split(",")}
/>
{/*<Space wrap>*/}
{/* <text>单次耗时:</text>*/}
{/* <Select*/}
{/* style={{width: 120}}*/}
{/* allowClear*/}
{/* defaultValue={onceConsumeChange[0]}*/}
{/* onChange={(value) => {*/}
{/* onceConsumeChange[0] = value*/}
{/* setOnceConsumeChange(onceConsumeChange)*/}
{/* }}*/}
{/* options={onceConsumeList}*/}
{/* />*/}
{/* <Select*/}
{/* style={{width: 120}}*/}
{/* allowClear*/}
{/* defaultValue={onceConsumeChange[1]}*/}
{/* onChange={(value) => {*/}
{/* onceConsumeChange[1] = value*/}
{/* setOnceConsumeChange(onceConsumeChange)*/}
{/* }}*/}
{/* options={[{label: "分钟", value: "m"}, {label: "小时", value: "h"}, {*/}
{/* label: "天",*/}
{/* value: "d"*/}
{/* }]}*/}
{/* />*/}
{/*</Space>*/}
</div>}
<SettingCron canSetting={!editFormDisable} setCronFunction={() => {