|
|
@@ -1,6 +1,6 @@
|
|
|
import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
-import { Plus, Search, Edit2, Trash2, MoreVertical, FileText, Eye, Play, CheckCircle, RefreshCw, Workflow } from 'lucide-react';
|
|
|
+import { Plus, Search, Edit2, Trash2, MoreVertical, FileText, Eye, Play, CheckCircle, RefreshCw, Workflow, Hand } from 'lucide-react';
|
|
|
import { Button, Input, Space, Select, Table as AntdTable, message, Modal, Form, Row, Col, Tabs, Radio, DatePicker, Checkbox, Tooltip } from 'antd';
|
|
|
import { Button as UIButton } from './ui/button';
|
|
|
import type { ColumnsType } from 'antd/es/table';
|
|
|
@@ -3127,6 +3127,46 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
};
|
|
|
};
|
|
|
|
|
|
+ // 获取紧急程度样式
|
|
|
+ const getUrgencyLevelStyle = (urgencyText: string): React.CSSProperties => {
|
|
|
+ if (!urgencyText || urgencyText === '-') {
|
|
|
+ return {
|
|
|
+ backgroundColor: '#e5e5e5',
|
|
|
+ color: '#333333',
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ const urgencyTextLower = urgencyText.toLowerCase();
|
|
|
+
|
|
|
+ // 正常:蓝色 #1677ff
|
|
|
+ if (urgencyTextLower.includes('正常') || urgencyTextLower.includes('normal') || urgencyTextLower.includes('普通')) {
|
|
|
+ return {
|
|
|
+ backgroundColor: '#1677ff',
|
|
|
+ color: '#ffffff',
|
|
|
+ };
|
|
|
+ }
|
|
|
+ // 紧急:橙色 #fa8c16
|
|
|
+ if (urgencyTextLower.includes('紧急') || urgencyTextLower.includes('urgent')) {
|
|
|
+ return {
|
|
|
+ backgroundColor: '#fa8c16',
|
|
|
+ color: '#ffffff',
|
|
|
+ };
|
|
|
+ }
|
|
|
+ // 非常紧急:红色 #ff4d4f
|
|
|
+ if (urgencyTextLower.includes('非常紧急') || urgencyTextLower.includes('very urgent') || urgencyTextLower.includes('特急')) {
|
|
|
+ return {
|
|
|
+ backgroundColor: '#ff4d4f',
|
|
|
+ color: '#ffffff',
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没有匹配到,默认灰色
|
|
|
+ return {
|
|
|
+ backgroundColor: '#e5e5e5',
|
|
|
+ color: '#333333',
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
// 作业管理表格列配置
|
|
|
const workJobColumns: ColumnsType<WorkJobVO> = [
|
|
|
{
|
|
|
@@ -3174,7 +3214,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
{
|
|
|
title: '作业内容',
|
|
|
dataIndex: 'description',
|
|
|
- width: '26%',
|
|
|
+ width: '20%',
|
|
|
render: (description: string, record: WorkJobVO) => {
|
|
|
const content = description || record.content || '-';
|
|
|
if (content === '-') return content;
|
|
|
@@ -3191,10 +3231,10 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
- title: '当前节点',
|
|
|
- dataIndex: 'currentNodeId',
|
|
|
+ title: '当前任务',
|
|
|
+ dataIndex: 'currentNodeName',
|
|
|
width: '12%',
|
|
|
- render: (currentNodeId: string, record: WorkJobVO) => currentNodeId || record.currentNode || '-',
|
|
|
+ render: (currentNodeName: string, record: WorkJobVO) => currentNodeName || record.currentNode || '-',
|
|
|
},
|
|
|
{
|
|
|
title: '作业状态',
|
|
|
@@ -3213,6 +3253,30 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
);
|
|
|
},
|
|
|
},
|
|
|
+ {
|
|
|
+ title: '紧急程度',
|
|
|
+ dataIndex: 'urgencyLevel',
|
|
|
+ minWidth: '180px',
|
|
|
+ align: 'center',
|
|
|
+ render: (urgencyLevel: string | number | undefined, record: WorkJobVO) => {
|
|
|
+ const urgencyValue = urgencyLevel || record.urgency || record.urgencyLevel;
|
|
|
+ const urgencyItem = urgencyLevelDictList.find(item => String(item.value) === String(urgencyValue));
|
|
|
+ const urgencyText = urgencyItem ? (urgencyItem.label || '') : (urgencyValue ? String(urgencyValue) : '-');
|
|
|
+
|
|
|
+ if (!urgencyValue || urgencyText === '-') {
|
|
|
+ return <span>-</span>;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <span
|
|
|
+ className="inline-flex px-3 py-1 rounded-lg text-xs"
|
|
|
+ style={getUrgencyLevelStyle(urgencyText)}
|
|
|
+ >
|
|
|
+ {urgencyText}
|
|
|
+ </span>
|
|
|
+ );
|
|
|
+ },
|
|
|
+ },
|
|
|
{
|
|
|
title: '操作',
|
|
|
width: '20%',
|
|
|
@@ -5260,7 +5324,7 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
</div>
|
|
|
|
|
|
{/* 底部按钮 */}
|
|
|
- <div className="px-6 py-4 border-t border-gray-200 flex justify-end gap-2">
|
|
|
+ <div className="px-6 py-4 border-t border-gray-200 flex justify-center gap-2">
|
|
|
{workJobStep === 0 && (
|
|
|
<>
|
|
|
<Button onClick={() => {
|
|
|
@@ -5428,65 +5492,112 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
</>
|
|
|
)}
|
|
|
{workJobStep === 1 && (
|
|
|
- <div className="flex items-center justify-between w-full">
|
|
|
- <div className="flex items-center text-red-600 text-sm">
|
|
|
- <span className="mr-1">⚠️</span>
|
|
|
- <span>提示:流程中的每一个节点都需要单独配置并保存,方可点击 "下一步"。</span>
|
|
|
- </div>
|
|
|
+ <div className="flex flex-col items-center w-full gap-2">
|
|
|
<div className="flex items-center gap-2">
|
|
|
<Button onClick={() => setWorkJobStep(0)}>
|
|
|
上一步
|
|
|
</Button>
|
|
|
{!isViewMode && (
|
|
|
<>
|
|
|
- <Button
|
|
|
- type="primary"
|
|
|
- disabled={hasUnsavedNodes}
|
|
|
- onClick={async () => {
|
|
|
- // 调用检查接口,检查是否有未保存的节点
|
|
|
- if (!workflowWorkId) {
|
|
|
- message.warning('作业ID不存在,无法检查');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- const checkResponse = await workJobApi.checkWorkById(workflowWorkId);
|
|
|
- const checkData = (checkResponse as any)?.data || checkResponse;
|
|
|
- const errorMessages = Array.isArray(checkData) ? checkData : (checkData ? [checkData] : []);
|
|
|
+ <div className="flex items-center gap-2">
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ disabled={hasUnsavedNodes}
|
|
|
+ className={!hasUnsavedNodes ? 'next-step-button-focus' : ''}
|
|
|
+ onClick={async () => {
|
|
|
+ // 调用检查接口,检查是否有未保存的节点
|
|
|
+ if (!workflowWorkId) {
|
|
|
+ message.warning('作业ID不存在,无法检查');
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- // 如果返回的错误信息数组为空或null,表示所有节点配置完毕,可以进入下一个tab
|
|
|
- if (!errorMessages || errorMessages.length === 0) {
|
|
|
- message.success('所有节点配置已完成');
|
|
|
- // 进入下一个tab
|
|
|
- setWorkJobStep(2);
|
|
|
- } else {
|
|
|
- // 有错误信息,显示弹框,不能进入下一个tab
|
|
|
- Modal.warning({
|
|
|
- title: '节点配置未完成',
|
|
|
- width: 500,
|
|
|
- content: (
|
|
|
- <div style={{ marginTop: 16 }}>
|
|
|
- <p style={{ marginBottom: 12, color: '#666' }}>以下节点配置不完整,请检查:</p>
|
|
|
- <ul style={{ margin: 0, paddingLeft: 20 }}>
|
|
|
- {errorMessages.map((msg: string, index: number) => (
|
|
|
- <li key={index} style={{ marginBottom: 8, color: '#ff4d4f' }}>
|
|
|
- {msg}
|
|
|
- </li>
|
|
|
- ))}
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
- ),
|
|
|
- okText: '我知道了',
|
|
|
- });
|
|
|
+ try {
|
|
|
+ const checkResponse = await workJobApi.checkWorkById(workflowWorkId);
|
|
|
+ const checkData = (checkResponse as any)?.data || checkResponse;
|
|
|
+ const errorMessages = Array.isArray(checkData) ? checkData : (checkData ? [checkData] : []);
|
|
|
+
|
|
|
+ // 如果返回的错误信息数组为空或null,表示所有节点配置完毕,可以进入下一个tab
|
|
|
+ if (!errorMessages || errorMessages.length === 0) {
|
|
|
+ message.success('所有节点配置已完成');
|
|
|
+ // 进入下一个tab
|
|
|
+ setWorkJobStep(2);
|
|
|
+ } else {
|
|
|
+ // 有错误信息,显示弹框,不能进入下一个tab
|
|
|
+ Modal.warning({
|
|
|
+ title: '节点配置未完成',
|
|
|
+ width: 500,
|
|
|
+ content: (
|
|
|
+ <div style={{ marginTop: 16 }}>
|
|
|
+ <p style={{ marginBottom: 12, color: '#666' }}>以下节点配置不完整,请检查:</p>
|
|
|
+ <ul style={{ margin: 0, paddingLeft: 20 }}>
|
|
|
+ {errorMessages.map((msg: string, index: number) => (
|
|
|
+ <li key={index} style={{ marginBottom: 8, color: '#ff4d4f' }}>
|
|
|
+ {msg}
|
|
|
+ </li>
|
|
|
+ ))}
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+ okText: '我知道了',
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error('检查作业失败:', error);
|
|
|
+ message.error(error?.message || '检查作业失败');
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ 下一步
|
|
|
+ </Button>
|
|
|
+ {!hasUnsavedNodes && (
|
|
|
+ <img
|
|
|
+ src={new URL('../assets/finger.png', import.meta.url).href}
|
|
|
+ alt="指向下一步"
|
|
|
+ className="pointer-hint-icon"
|
|
|
+ style={{
|
|
|
+ width: '32px',
|
|
|
+ height: '32px',
|
|
|
+ animation: 'pointing 1.5s ease-in-out infinite',
|
|
|
+ display: 'inline-block',
|
|
|
+ cursor: 'pointer',
|
|
|
+ marginLeft: '8px',
|
|
|
+ objectFit: 'contain'
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ <style>{`
|
|
|
+ @keyframes focusPulse {
|
|
|
+ 0%, 100% {
|
|
|
+ box-shadow: 0 0 0 0 rgba(22, 119, 255, 0.7);
|
|
|
+ transform: scale(1);
|
|
|
+ }
|
|
|
+ 50% {
|
|
|
+ box-shadow: 0 0 0 10px rgba(22, 119, 255, 0);
|
|
|
+ transform: scale(1.05);
|
|
|
}
|
|
|
- } catch (error: any) {
|
|
|
- console.error('检查作业失败:', error);
|
|
|
- message.error(error?.message || '检查作业失败');
|
|
|
}
|
|
|
- }}
|
|
|
- >
|
|
|
- 下一步
|
|
|
- </Button>
|
|
|
+ .next-step-button-focus {
|
|
|
+ animation: focusPulse 2s ease-in-out infinite;
|
|
|
+ }
|
|
|
+ @keyframes pointing {
|
|
|
+ 0%, 100% {
|
|
|
+ transform: translateX(0);
|
|
|
+ }
|
|
|
+ 25% {
|
|
|
+ transform: translateX(-4px);
|
|
|
+ }
|
|
|
+ 50% {
|
|
|
+ transform: translateX(0);
|
|
|
+ }
|
|
|
+ 75% {
|
|
|
+ transform: translateX(4px);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .pointer-hint-icon {
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ `}</style>
|
|
|
+ </div>
|
|
|
{/* <Button
|
|
|
type="default"
|
|
|
onClick={async () => {
|
|
|
@@ -5546,6 +5657,10 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
</Button>
|
|
|
)}
|
|
|
</div>
|
|
|
+ <div className="flex items-center text-red-600" style={{ fontSize: '12px' }}>
|
|
|
+ <span className="mr-1">⚠️</span>
|
|
|
+ <span>提示:流程中的每一个节点都需要单独配置并保存,方可点击 "下一步"。</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
)}
|
|
|
{workJobStep === 2 && (
|
|
|
@@ -5674,34 +5789,51 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
|
|
|
setEditingItem(null);
|
|
|
form.resetFields();
|
|
|
}}
|
|
|
- onOk={async () => {
|
|
|
- try {
|
|
|
- const values = await form.validateFields();
|
|
|
- await workJobApi.updateWorkflowWork({
|
|
|
- id: editingItem.id!,
|
|
|
- name: values.name,
|
|
|
- type: values.type || editingItem.type || '',
|
|
|
- designId: values.designId || editingItem.designId || editingItem.workflowDesignId || 0,
|
|
|
- description: values.content || '',
|
|
|
- urgencyLevel: values.urgencyLevel || editingItem.urgencyLevel || '',
|
|
|
- });
|
|
|
- message.success('更新作业成功');
|
|
|
+ footer={[
|
|
|
+ <Button key="cancel" onClick={() => {
|
|
|
setShowAddModal(false);
|
|
|
setEditingItem(null);
|
|
|
form.resetFields();
|
|
|
- getWorkJobList();
|
|
|
- } catch (error: any) {
|
|
|
- if (error.errorFields) {
|
|
|
- return;
|
|
|
+ }}>
|
|
|
+ 取消
|
|
|
+ </Button>,
|
|
|
+ <Button key="submit" type="primary" onClick={async () => {
|
|
|
+ try {
|
|
|
+ const values = await form.validateFields();
|
|
|
+ await workJobApi.updateWorkflowWork({
|
|
|
+ id: editingItem.id!,
|
|
|
+ name: values.name,
|
|
|
+ type: values.type || editingItem.type || '',
|
|
|
+ designId: values.designId || editingItem.designId || editingItem.workflowDesignId || 0,
|
|
|
+ description: values.content || '',
|
|
|
+ urgencyLevel: values.urgencyLevel || editingItem.urgencyLevel || '',
|
|
|
+ });
|
|
|
+ message.success('更新作业成功');
|
|
|
+ setShowAddModal(false);
|
|
|
+ setEditingItem(null);
|
|
|
+ form.resetFields();
|
|
|
+ getWorkJobList();
|
|
|
+ } catch (error: any) {
|
|
|
+ if (error.errorFields) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ console.error('更新作业失败:', error);
|
|
|
+ message.error(error?.message || '更新作业失败');
|
|
|
}
|
|
|
- console.error('更新作业失败:', error);
|
|
|
- message.error(error?.message || '更新作业失败');
|
|
|
- }
|
|
|
- }}
|
|
|
- okText="确定"
|
|
|
- cancelText="取消"
|
|
|
+ }}>
|
|
|
+ 确定
|
|
|
+ </Button>
|
|
|
+ ]}
|
|
|
width={600}
|
|
|
destroyOnClose
|
|
|
+ styles={{
|
|
|
+ footer: {
|
|
|
+ display: 'flex',
|
|
|
+ justifyContent: 'center',
|
|
|
+ alignItems: 'center',
|
|
|
+ padding: '16px 24px'
|
|
|
+ }
|
|
|
+ }}
|
|
|
>
|
|
|
<Form
|
|
|
form={form}
|