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{ .localDiv{
margin-bottom: 24px; margin-bottom: 24px;
}
.icon{
font-size: 1rem;
font-color: orange;
} }

View File

@ -1,7 +1,9 @@
import styles from "@/components/TaskRemind.module.css"; import styles from "@/components/TaskRemind.module.css";
import {CascaderProps, ConfigProvider} from 'antd'; import {CascaderProps, ConfigProvider, message} from 'antd';
import {Cascader} 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 { interface ITaskRemind {
remindTypeList: string[], remindTypeList: string[],
@ -9,14 +11,7 @@ interface ITaskRemind {
readonly: boolean, readonly: boolean,
} }
interface Option { const optionLists: CascaderOption[] = [
value?: string | number | null;
label: React.ReactNode;
children?: Option[];
isLeaf?: boolean;
}
const optionLists: Option[] = [
{ {
label: "期望开始", label: "期望开始",
value: "expect_start", value: "expect_start",
@ -30,11 +25,54 @@ const optionLists: Option[] = [
]; ];
const TaskRemindComponent = (props: ITaskRemind) => { const TaskRemindComponent = (props: ITaskRemind) => {
const [options, setOptions] = useState<Option[]>(optionLists); const [options, setOptions] = useState<CascaderOption[]>(optionLists);
const cascaderOnChange: CascaderProps<Option>['onChange'] = (value: (string | number)[], selectedOptions: Option[]) => { // : CascaderProps<Option>['onChange']
console.log(value, selectedOptions); 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]; const targetOption = selectedOptions[selectedOptions.length - 1];
if (targetOption.value === "expect_start" || targetOption.value === "expect_end") { if (targetOption.value === "expect_start" || targetOption.value === "expect_end") {
targetOption.children = [ targetOption.children = [
@ -51,7 +89,8 @@ const TaskRemindComponent = (props: ITaskRemind) => {
]; ];
} else if (targetOption.value === "before" || targetOption.value === "after") { } else if (targetOption.value === "before" || targetOption.value === "after") {
targetOption.children = Array.from({length: 60}, (_, i) => ({ 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, value: i,
isLeaf: false, isLeaf: false,
})) }))
@ -65,12 +104,12 @@ const TaskRemindComponent = (props: ITaskRemind) => {
{ {
label: "小时", label: "小时",
value: "h", value: "h",
isLeaf: false, isLeaf: true,
}, },
{ {
label: "天", label: "天",
value: "d", value: "d",
isLeaf: false, isLeaf: true,
} }
] ]
} }
@ -143,7 +182,7 @@ const TaskRemindComponent = (props: ITaskRemind) => {
<div>-</div> <div>-</div>
) : ( ) : (
props.remindTypeList.map((remindType, index) => ( props.remindTypeList.map((remindType, index) => (
<div key={index}>{remindType.split(",").join(", ")}</div> <div key={index}>{changeValueToLabel(remindType)}</div>
)) ))
) )
) : props.remindTypeList.length === 0 ? ( ) : props.remindTypeList.length === 0 ? (
@ -152,16 +191,42 @@ const TaskRemindComponent = (props: ITaskRemind) => {
components: { components: {
Cascader: { Cascader: {
/* 这里是你的组件 token */ /* 这里是你的组件 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> </ConfigProvider>
) : ( ) : (
props.remindTypeList.map((remindType, index) => ( 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>) </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, value: any,
operateType:string, operateType:string,
} }
export interface CascaderOption {
value?: string | number | null;
label: React.ReactNode;
children?: CascaderOption[];
isLeaf?: boolean;
}

View File

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