/* eslint-disable no-param-reassign */ /* eslint-disable array-callback-return */ import { AddMission } from '@/components/DragModal/formdoms'; import IconFont from '@/components/IconFont'; import { doFetch, getFetch } from '@/utils/doFetch'; import { ArrowLeftOutlined, DeleteOutlined, PlayCircleFilled, PlusOutlined, RedoOutlined, TagOutlined, } from '@ant-design/icons'; import { history, useLocation } from '@umijs/max'; import { useRequest } from 'ahooks'; import { Avatar, Button, Card, Modal, Popconfirm, Segmented, Select, Skeleton, Tooltip, Typography, } from 'antd'; import dayjs from 'dayjs'; import _ from 'lodash'; import { useState } from 'react'; import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; import { AddSteps, AddTags } from '../../components/DragModal/formdoms'; import './index.less'; import QuoteList from './QuoteList'; const { Paragraph } = Typography; const items = { 0: { value: '0', statusName: '未开始', icon: ( <Tooltip title="点击开始"> <PlayCircleFilled style={{ color: '#1890ff' }} /> </Tooltip> ), }, 1: { value: '1', statusName: '进行中', color: 'green', icon: ( <Tooltip title="点击完成"> <IconFont type="icon-jinhangzhong-shijian-daojishi-03" style={{ color: 'green' }} /> </Tooltip> ), }, 2: { value: '2', statusName: '已完成', color: 'grey', icon: ( <Tooltip title="已完成"> <IconFont type="icon-yiwancheng" style={{ color: 'grey' }} /> </Tooltip> ), }, }; const columns = []; function swapArray(arr, index1, index2) { arr[index1] = arr.splice(index2, 1, arr[index1])[0]; return arr; } const Project = () => { const location = useLocation(); const id = location.state.id; const [state, setState] = useState({ columns, search: { status: '-1' } }); const [modal, setmodal] = useState({ open: false, }); const { data, loading, run } = useRequest( async () => { const res = await getFetch({ url: '/webtool/v1/project/' + id, params: { ...state?.search }, }); return res?.data; }, { debounceWait: 300, refreshDeps: [state.search], onSuccess: (data) => { setState((s) => ({ ...s, columns: data?.steps, })); }, }, ); const onDragEnd = (result) => { // the only one that is required const { destination, source, draggableId, combine } = result; if (result.type === 'COLUMN') { const submitarr = Object?.values?.(state?.columns); let resarr = swapArray(submitarr, source.index, destination.index); resarr = resarr.map((it, i) => ({ ...it, sort: i, })); setState((s) => ({ ...s, columns: resarr, })); resarr?.map((it, i) => { doFetch({ url: '/webtool/v1/step/' + it?.id, params: { sort: i, }, method: 'PUT', }); }); return; } if (!destination) { return; } if (destination.droppableId === source.droppableId && destination.index === source.index) { return; } const firstarrindex = state.columns ?.map((it) => it?.id?.toString()) .indexOf(source.droppableId); const secondarrindex = state.columns ?.map((it) => it?.id?.toString()) .indexOf(destination.droppableId); if (firstarrindex === secondarrindex) { let id1, id2, sort1, sort2; setState((s) => { let newColumns = JSON.parse(JSON.stringify(s.columns)); newColumns = newColumns?.map((item, index) => { if (index === firstarrindex) { let donate = JSON.parse(JSON.stringify(item.items)); return { ...item, items: item.items .map((it, i) => { if (i === source.index) { id1 = it?.id; sort1 = donate[destination.index].sort; it.sort = sort1; } if (i === destination.index) { id2 = it?.id; sort2 = donate[source.index].sort; it.sort = sort2; } return it; }) .sort((a, b) => a.sort - b.sort), }; } return item; }); return { ...s, columns: newColumns, }; }); doFetch({ url: '/webtool/v1/item/' + id1, params: { sort: sort1, }, method: 'PUT', }); doFetch({ url: '/webtool/v1/item/' + id2, params: { sort: sort2, }, method: 'PUT', }); } else { let moveItem, donate; setState((s) => { let newColumns = JSON.parse(JSON.stringify(s.columns)); newColumns = newColumns ?.map((item, index) => { if (index === firstarrindex) { return { ...item, items: item.items.filter((it, i) => { if (i === source.index) { moveItem = it; } return i !== source.index; }), }; } return item; }) .map((item, index) => { if (index === secondarrindex) { donate = JSON.parse(JSON.stringify(item.items)); donate.splice(destination.index, 0, { ...moveItem, step_id: item.id }); return { ...item, items: donate?.map((it, i) => ({ ...it, sort: i, })), }; } return item; }); return { ...s, columns: newColumns, }; }); const sortlist = donate?.map((it, i) => _.pick( { ...it, }, ['id', 'step_id'], ), ); doFetch({ url: '/webtool/v1/mutisort', params: { sortlist, }, }).then((res) => { if (res.code === 0) { Modal.confirm({ title: '修改执行人', maskClosable: true, content: ( <div> <Select style={{ width: '100%' }} placeholder={'修改执行人'} options={data?.user_info_list?.map((it) => { return { label: it?.user_name, value: it?.id, }; })} onChange={(value) => { doFetch({ url: '/webtool/v1/item/' + moveItem.id, params: { userid: value }, method: 'PUT', }).then((res) => { if (res.code === 0) { Modal.destroyAll(); run(); } }); }} ></Select> </div> ), footer: false, }); } }); } }; return ( <DragDropContext onDragEnd={onDragEnd}> <Modal {...modal} onCancel={() => { setmodal((s) => ({ ...s, open: false, })); }} footer={false} destroyOnClose={true} > {['新建任务', '编辑任务'].includes(modal?.title) && ( <AddMission project_id={id} sort={modal?.sort} step_id={modal?.step_id} defaultValue={modal?.defaultValue} userList={data?.user_info_list} enddate={data?.deadline} refresh={() => { run(); setmodal((s) => ({ ...s, open: false, })); }} /> )} {modal?.title === '新建流程' && ( <AddSteps id={id} columns={state?.columns} refresh={() => { run(); setmodal((s) => ({ ...s, open: false, })); }} /> )} {modal?.title === '管理标签' && ( <AddTags project_id={id} refresh={() => { run(); setmodal((s) => ({ ...s, open: false, })); }} /> )} </Modal> <div className="center bglight" style={{ justifyContent: 'space-between', margin: '0px 0 12px 0', padding: 12, borderRadius: 12, }} > {loading && !data ? ( <Skeleton.Input active /> ) : ( <> <div className="center"> <div className="hoverable" onClick={() => { history.go(-1); }} > <ArrowLeftOutlined /> </div> <b style={{ textIndent: 18 }}>{data?.project_name}</b> <Tooltip title={dayjs(data?.created_at).format('YYYY-MM-DD HH:mm')}> <b className="center" style={{ color: '#333', textIndent: 12, marginRight: 18 }}> 创建于 {dayjs().diff(dayjs(data?.created_at), 'hour') > 24 ? `${dayjs().diff(dayjs(data?.created_at), 'day')}天前` : dayjs().diff(dayjs(data?.created_at), 'hour') < 1 ? `${dayjs().diff(dayjs(data?.created_at), 'minute')}分钟前` : `${dayjs().diff(dayjs(data?.created_at), 'hour')}小时前`} </b> </Tooltip> <Tooltip title={'刷新'}> <Button icon={<RedoOutlined />} style={{ marginRight: 12 }} onClick={() => { run(); }} /> </Tooltip> <Segmented value={state.search.status} onChange={async (val) => { await setState((s) => ({ ...s, search: { ...s.search, status: val, }, })); }} options={[ { label: '全部', value: '-1', }, ].concat( Object.values(items).map((it) => ({ label: it.statusName, value: it.value, })), )} /> </div> <div className="center" style={{ gap: 12 }}> <Button icon={<TagOutlined />} type="text" ghost size="30px" onClick={() => { setmodal((s) => ({ ...s, title: '管理标签', open: true, })); }} ></Button> <Avatar.Group> {data?.user_info_list?.map((it, i) => { if (it?.head_url && it?.head_url !== '') { return ( <Tooltip title={it?.user_name} key={i}> <Avatar src={it?.head_url} /> </Tooltip> ); } else { return ( <Tooltip title={it?.user_name} key={i}> <Avatar>{it?.user_name?.charAt(0)}</Avatar> </Tooltip> ); } })} </Avatar.Group> </div> </> )} </div> <Skeleton active loading={loading && !data}> <Droppable droppableId={'board'} direction="horizontal" type="COLUMN"> {(provided, snapshot) => ( <div {...provided.droppableProps} ref={provided.innerRef} style={{ width: '100%', display: 'flex', justifyContent: 'flex-start', alignItems: 'flex-start', }} > {state.columns .sort((a, b) => a.sort - b.sort) .map((column, index) => ( <Draggable key={column?.id?.toString()} draggableId={column?.id?.toString()} index={index} > {(provided, snapshot) => ( <Card title={ <Paragraph {...provided.dragHandleProps} className="no-outline" editable={{ onChange: async (value) => { let res = await doFetch({ url: '/webtool/v1/step/' + column?.id, params: { name: value, }, method: 'PUT', }); if (res.code === 0) { run(); } }, }} > {column?.name} </Paragraph> } className="card" ref={provided.innerRef} {...provided.draggableProps} hoverable extra={ column?.items?.length === 0 && ( <Popconfirm title="是否删除该流程?" placement="bottomRight" onConfirm={async () => { let res = await doFetch({ url: '/webtool/v1/step/' + column.id, method: 'DELETE', }); if (res.code === 0) { run(); } }} > <DeleteOutlined style={{ color: '#ff4800' }} /> </Popconfirm> ) } > <QuoteList userList={data?.user_info_list} refresh={() => { run(); }} add={() => { setmodal((s) => ({ ...s, open: true, title: '新建任务', width: '1000px', step_id: column.id, sort: column?.items?.length ? Math.max(...column?.items?.map((it) => it.sort + 1)) : 0, })); }} edit={(defaultValue) => { setmodal((s) => ({ ...s, open: true, title: '编辑任务', width: '1000px', step_id: column.id, defaultValue: defaultValue, sort: null, })); }} datas={column?.items ?? []} listId={column?.id?.toString()} ></QuoteList> </Card> )} </Draggable> ))} {!snapshot.draggingFromThisWith && ( <Card style={{ height: 50 }} hoverable className="center" onClick={() => { setmodal((s) => ({ ...s, open: true, title: '新建流程', width: '400px', })); }} > <PlusOutlined style={{ color: '#1890ff' }} /> </Card> )} </div> )} </Droppable> </Skeleton> </DragDropContext> ); }; export default Project;