import * as React from 'react'; import * as ReactDOM from 'react-dom'; import SideList from './components/SiderList'; import { fetchAllTasks } from './api/index'; import { RuningTaskData, FinishTaskData } from './modal'; import NoticeQueue from './utils/noticeQueue'; import { message, notification, Icon } from 'antd'; import './index.less'; // import * as ReactMotion from 'react-motion'; // const { Motion, spring } = ReactMotion; // const springSetting = {stiffness: 180, damping: 10}; // const [width] = [60, 60]; // 全局轮询id let timeId: number; // 列表数据轮询间隔 todo: 支持websoket推送 const DEFAULT_POLLING_INTERVAL = 1000; // notice弹出间隔 const NOTICE_INTERVAL = 1000; export interface ConfirmFuncProps { /** 轮询间隔 */ interval?: number; onCancel?: (...args: any[]) => any | PromiseLike; data: { running_tasks: any[], finish_tasks: any[] } } interface ConfirmDialogProps extends ConfirmFuncProps { afterClose?: () => void; close: (...args: any[]) => void; employee_id: number; } interface Position { pageX: number, pageY: number } type InitialState = { showBar: boolean; fadeClassName: string; data: { running_tasks: RuningTaskData[]; finish_tasks: FinishTaskData[] }; newTasks: RuningTaskData[]; failTasks: FinishTaskData[]; sucTasks: FinishTaskData[]; isPressed: boolean; mouseXY: [number, number]; premouseXY: [number, number]; mouseCircleDelta: [number, number]; isMove: boolean; } const AddNotice = ({taskData }: {taskData: RuningTaskData}) => { notification.open({ key: `task_center_${taskData.id}`, duration: null, message: `任务添加成功(${taskData.name})`, description: taskData.description, icon: , }); return { close: () => notification.close(`task_center_${taskData.id}`) } } const FinishNotice = ({taskData }: {taskData: FinishTaskData}) => { let type,icon; switch (taskData.status) { case 4: type = '取消'; icon = 'info'; break; case 5: type = '失败'; icon = 'fail'; break; case 6: type = '超时'; icon = 'clock'; break; case 7: type = '完成'; icon = 'check'; break; default: type = '完成'; icon = 'check'; } notification.open({ key: `task_center_${taskData.id}`, duration: null, message: `任务${type}成功(${taskData.name})`, description: taskData.description, icon: , }); return { close: () => notification.close(`task_center_${taskData.id}`) } } class ConfirmDialog extends React.Component { state:InitialState = { showBar: false, fadeClassName: '', data: { running_tasks: [], finish_tasks: [] }, newTasks: [], failTasks: [], sucTasks: [], isPressed: false, mouseXY: [0, 0], premouseXY: [0, 0], mouseCircleDelta: [0, 0], isMove: false, // 是否是移动点击 } noticeQueue = new NoticeQueue({ interval: NOTICE_INTERVAL }); async componentDidMount() { window.addEventListener('touchmove', this.handleTouchMove); window.addEventListener('mousemove', this.handleMouseMove); window.addEventListener('touchend', this.handleMouseUp); window.addEventListener('mouseup', this.handleMouseUp); await this.fetchAllData('init'); // 启动遍历 this.noticeQueue.start(); } async fetchAllData(type?: string) { // 调用fetchAllData,立即发起请求。 此后的循环请求必须在上个请求完成后1秒请求 const { employee_id } = this.props; if(timeId) { clearTimeout(timeId) } this.setState({ loading: true }); const { code, data } = await fetchAllTasks(employee_id, { errorCb: (msg: string) => { message.error(msg); } }); this.setState({ loading: false }) if( code === 200 ) { const pre_data = this.state.data; const pre_running_tasks = pre_data.running_tasks; const pre_finish_tasks = pre_data.finish_tasks; const running_tasks = data.running_tasks || []; const finish_tasks = data.finish_tasks || []; // 新增任务 const newTasks = running_tasks.reduce((acc:RuningTaskData[], cur:RuningTaskData) => { const isNew = pre_running_tasks.every(task => task.id !== cur.id); if(isNew) acc.push(cur) return acc }, []); // 新结束的任务 const finishTaskData = finish_tasks.reduce((acc: { failTasks: FinishTaskData[], sucTasks: FinishTaskData[] } , cur:FinishTaskData) => { const isNew = pre_finish_tasks.every(task => task.id !== cur.id); if(isNew) { if(cur.status == 7) { acc.sucTasks.push(cur) } else { acc.failTasks.push(cur) } } return acc }, { failTasks:[], sucTasks: []}); // 失败 & 成功的任务 const { failTasks, sucTasks } = finishTaskData; if (newTasks.length && type !== 'init') { const cbList = newTasks.map((task: RuningTaskData) => { return () => AddNotice({taskData: task}); }) this.noticeQueue.push(cbList); } const finishTasks = finishTaskData.failTasks.concat(finishTaskData.sucTasks); if (finishTasks.length && type !== 'init') { const cbList = finishTasks.map((task: FinishTaskData) => { return () => FinishNotice({taskData: task}); }) this.noticeQueue.push(cbList); } this.setState({ data: { running_tasks: running_tasks || [], finish_tasks: finish_tasks || [] }, newTasks, failTasks, sucTasks }) } // 要使用全称window.setTimeout, 单独使用setTimeout会报类型错误 timeId = window.setTimeout(() => { this.fetchAllData() }, DEFAULT_POLLING_INTERVAL); } handleShowBar = () => { const { isMove } = this.state; if(!isMove) { this.setState({ showBar: true, fadeClassName: 'fadeInRight' }); } } handleCloseBar= () => { this.setState({ showBar: false, fadeClassName: 'fadeOutRight' }); } handleMouseDown = ([pressX, pressY]: number[], {pageX, pageY}: Position) => { // mouseCircleDelta 鼠标点击与位置的偏差 const { mouseXY } = this.state; this.setState({ isPressed: true, mouseCircleDelta: [pageX - pressX, pageY - pressY], premouseXY: mouseXY, isMove: false }); } handleTouchStart = (pressLocation: [number, number], e: Event) => { // @ts-ignore:忽略touch 事件对象没有touches异常 this.handleMouseDown( pressLocation, e.touches[0]); } handleTouchMove = (e: Event) => { e.preventDefault(); // @ts-ignore:忽略touch 事件对象没有touches异常 this.handleMouseMove(e.touches[0]); }; handleMouseMove = ({pageX, pageY}: Position) => { const {isPressed, mouseCircleDelta: [dx, dy]} = this.state; if (isPressed) { const mouseXY = [pageX - dx, pageY - dy]; this.setState({mouseXY}); } }; handleMouseUp = () => { const { premouseXY:[ preX, preY], mouseXY: [x, y] } = this.state; // 直线移动 2px , 为移动事件,不触发点击事件 const isMove = (preX - x) * (preX - x) + (preY - y) * (preY - y) > 2 * 2; this.setState({isPressed: false, isMove}); }; render() { const { employee_id } = this.props; const { showBar, fadeClassName, mouseXY, data, isPressed } = this.state; const [x, y] = mouseXY; return (
{ data.running_tasks.length > 0 && {data.running_tasks.length} }
{ !!fadeClassName && }
); } }; let div: null | Element; export default function confirm (config: ConfirmFuncProps) { if (!div) { div = document.createElement('div'); document.body.appendChild(div); } function handleRender(props: any) { ReactDOM.render(, div); } function destroy(...args: any[]) { const unmountResult = ReactDOM.unmountComponentAtNode(div); if (unmountResult && div.parentNode && div) { div.parentNode.removeChild(div); } div = null; const triggerCancel = args && args.length && args.some(param => param && param.triggerCancel); if (config.onCancel && triggerCancel) { config.onCancel(...args); } } handleRender({ ...config }); return { destroy: destroy, ref: div } }