Compare commits
No commits in common. "94db0b4d2aa58b6fc328ec9b1ddf6ad5b6e22343" and "127772bd8824d4be3b042e92b0ca9e052dc8ad11" have entirely different histories.
94db0b4d2a
...
127772bd88
|
|
@ -286,14 +286,14 @@ def create_app():
|
||||||
async def flows_list(user: DB.User = Depends(get_current_user)):
|
async def flows_list(user: DB.User = Depends(get_current_user)):
|
||||||
global_tasks = app.state.tasks
|
global_tasks = app.state.tasks
|
||||||
global_status = app.state.task_nodes
|
global_status = app.state.task_nodes
|
||||||
db_filter = { "is_deleted": 0 }
|
db_filter = { "id_deleted": 0 }
|
||||||
if user.level != 0:
|
if user.level != 0:
|
||||||
work_ids = await DB.WorkUserRelation.filter(user_id=user.id).values_list("work_id", flat=True)
|
work_ids = await DB.WorkUserRelation.filter(user_id=user.id).values_list("work_id", flat=True)
|
||||||
db_filter["id__in": work_ids if work_ids else [0]]
|
db_filter["id__in": work_ids if work_ids else [0]]
|
||||||
work_list = await DB.Work.filter(**db_filter).order_by("-id")
|
work_list = await DB.Work.filter(**db_filter).order_by("-id")
|
||||||
work_ids = [x.id for x in work_list]
|
work_ids = [x.id for x in work_list]
|
||||||
work_content_list = await DB.WorkContent.filter(work_id__in=work_ids).all()
|
work_content_list = await DB.WorkContent.filter(work_id__in=work_ids).all()
|
||||||
sub_logs = await DB.WorkLogs.filter(work_id__in=work_ids).group_by('work_id').annotate(max_id=Max("id")).values_list("max_id", flat=True)
|
sub_logs = await DB.WorkLogs.filter(work_id__in=work_ids).annotate(max_id=Max("id")).values_list("max_id", flat=True)
|
||||||
work_logs_list = await DB.WorkLogs.filter(id__in=sub_logs).all()
|
work_logs_list = await DB.WorkLogs.filter(id__in=sub_logs).all()
|
||||||
user_list = await DB.User.filter(id__in=list(set([x.user_id for x in work_content_list]))).all()
|
user_list = await DB.User.filter(id__in=list(set([x.user_id for x in work_content_list]))).all()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { App, Button, Card, Col, Dropdown, message, Modal, Row, Space, Typography } from "antd"
|
import { App, Button, Card, Col, message, Modal, Row, Space, Typography } from "antd"
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
|
|
@ -18,7 +18,6 @@ import { flowCreate, flowDetail, flowUpdate } from "../../api/flow";
|
||||||
import EditNode from "./components/EditNode";
|
import EditNode from "./components/EditNode";
|
||||||
import CustomNode from "./components/CustomNode";
|
import CustomNode from "./components/CustomNode";
|
||||||
import { ProForm, ProFormText, ProFormTextArea } from "@ant-design/pro-components";
|
import { ProForm, ProFormText, ProFormTextArea } from "@ant-design/pro-components";
|
||||||
import { DownloadOutlined, UploadOutlined, EllipsisOutlined, ApartmentOutlined } from "@ant-design/icons";
|
|
||||||
|
|
||||||
const COLORS = {
|
const COLORS = {
|
||||||
first: "#89A8B2",
|
first: "#89A8B2",
|
||||||
|
|
@ -63,7 +62,6 @@ const FlowDetail = () => {
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
const [record, setRecord] = useState({});
|
const [record, setRecord] = useState({});
|
||||||
const editRef = useRef(null);
|
const editRef = useRef(null);
|
||||||
const inputRef = useRef(null);
|
|
||||||
const controlRef = useRef(null);
|
const controlRef = useRef(null);
|
||||||
const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), [setEdges]);
|
const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), [setEdges]);
|
||||||
const onBeforeDelete = async (params) => {
|
const onBeforeDelete = async (params) => {
|
||||||
|
|
@ -118,7 +116,7 @@ const FlowDetail = () => {
|
||||||
sourcePosition: 'right',
|
sourcePosition: 'right',
|
||||||
targetPosition: 'left',
|
targetPosition: 'left',
|
||||||
type: e.type,
|
type: e.type,
|
||||||
data: { label: e?.title || '开始' , style: { backgroundColor: COLORS[e.type] }, position: POSITION[e.type] },
|
data: { label: e.title, style: { backgroundColor: COLORS[e.type] }, position: POSITION[e.type] },
|
||||||
detail: e
|
detail: e
|
||||||
})
|
})
|
||||||
if (e.edge) {
|
if (e.edge) {
|
||||||
|
|
@ -128,9 +126,9 @@ const FlowDetail = () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
controlRef?.current?.setNodes(tempNodes);
|
|
||||||
controlRef?.current?.setEdges(tempEdges);
|
setNodes(tempNodes);
|
||||||
await controlRef?.current?.fitView();
|
setEdges(tempEdges);
|
||||||
} else {
|
} else {
|
||||||
message.error(res.message);
|
message.error(res.message);
|
||||||
}
|
}
|
||||||
|
|
@ -178,75 +176,6 @@ const FlowDetail = () => {
|
||||||
message.warning("请检查基础信息");
|
message.warning("请检查基础信息");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const downloadAction = () => {
|
|
||||||
modal.confirm({
|
|
||||||
title: '导出',
|
|
||||||
content: '导出当前工作配置?',
|
|
||||||
okText: '导出',
|
|
||||||
cancelText: '取消',
|
|
||||||
onOk: () => {
|
|
||||||
try {
|
|
||||||
const filename = new Date().getTime() + '.json'
|
|
||||||
const jsonStr = JSON.stringify({ nodes, edges }, null, 4)
|
|
||||||
const blob = new Blob([jsonStr], { type: "application/json;charset=utf-8" });
|
|
||||||
// IE / Edge (old) fallback
|
|
||||||
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
|
|
||||||
window.navigator.msSaveOrOpenBlob(blob, filename);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
const a = document.createElement("a");
|
|
||||||
a.href = url;
|
|
||||||
a.download = filename;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
a.remove();
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("导出失败", error);
|
|
||||||
message.error("导出失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
const uploadAction = () => {
|
|
||||||
inputRef?.current?.click();
|
|
||||||
}
|
|
||||||
const onFileInputChange = (event) => {
|
|
||||||
const fileInput = event.target;
|
|
||||||
if (fileInput?.files?.length === 0) return;
|
|
||||||
const file = fileInput.files[0];
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = (e) => {
|
|
||||||
try {
|
|
||||||
const text = e.target.result;
|
|
||||||
const jsonData = JSON.parse(text);
|
|
||||||
controlRef?.current?.setNodes(jsonData.nodes);
|
|
||||||
controlRef?.current?.setEdges(jsonData.edges);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("JSON 解析失败:", err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
reader.readAsText(file, "utf-8");
|
|
||||||
fileInput.value = "";
|
|
||||||
}
|
|
||||||
const formatAction = async () => {
|
|
||||||
const result = groupNodes(nodes, edges);
|
|
||||||
let y = 0;
|
|
||||||
let x = 0;
|
|
||||||
result?.forEach(rows => {
|
|
||||||
rows.forEach((e) => {
|
|
||||||
e.position = { x, y }
|
|
||||||
controlRef?.current?.updateNode(e.id);
|
|
||||||
x += 180
|
|
||||||
})
|
|
||||||
y += 80;
|
|
||||||
x = 0;
|
|
||||||
});
|
|
||||||
await controlRef?.current?.fitView();
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadDetail();
|
loadDetail();
|
||||||
|
|
@ -256,26 +185,8 @@ const FlowDetail = () => {
|
||||||
<Row gutter={[16, 0]}>
|
<Row gutter={[16, 0]}>
|
||||||
<Col span={18} >
|
<Col span={18} >
|
||||||
<Card title="工作" extra={<Space>
|
<Card title="工作" extra={<Space>
|
||||||
<div style={{ display: 'none' }}>
|
<Button onClick={() => navigate("/flow")}>取消</Button>
|
||||||
<input ref={inputRef} type="file" accept=".json" onChange={onFileInputChange} />
|
<Button type='primary' onClick={() => onSave()}>{isCreate ? '创建':'保存'}</Button>
|
||||||
</div>
|
|
||||||
<Button onClick={() => navigate("/flow")}>返回</Button>
|
|
||||||
<Dropdown menu={{
|
|
||||||
onClick: ({ key }) => {
|
|
||||||
if (key === 'download') downloadAction();
|
|
||||||
else if (key === 'upload') uploadAction();
|
|
||||||
else if (key === 'format') formatAction();
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{ key: 'format', label: '整理', icon: <ApartmentOutlined /> },
|
|
||||||
{ key: 'line', type: 'divider' },
|
|
||||||
{ key: 'download', label: '导出', icon: <DownloadOutlined /> },
|
|
||||||
{ key: 'upload', label: '导入', icon: <UploadOutlined /> },
|
|
||||||
]
|
|
||||||
}}>
|
|
||||||
<Button><EllipsisOutlined /></Button>
|
|
||||||
</Dropdown>
|
|
||||||
<Button type='primary' onClick={() => onSave()}>{isCreate ? '创建' : '保存'}</Button>
|
|
||||||
</Space>}>
|
</Space>}>
|
||||||
<div style={{ height: 'calc(100vh - 220px)', width: '100%' }}>
|
<div style={{ height: 'calc(100vh - 220px)', width: '100%' }}>
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
|
|
@ -345,36 +256,4 @@ const ControlButtons = ({ onLoad }) => {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|
||||||
function groupNodes(nodes, edges) {
|
|
||||||
const nodeMap = Object.fromEntries(nodes.map((n) => [n.id, n]));
|
|
||||||
const graph = {};
|
|
||||||
nodes.forEach((n) => (graph[n.id] = new Set()));
|
|
||||||
|
|
||||||
edges.forEach(({ source, target }) => {
|
|
||||||
graph[source].add(target);
|
|
||||||
graph[target].add(source);
|
|
||||||
});
|
|
||||||
|
|
||||||
const visited = new Set();
|
|
||||||
const result = [];
|
|
||||||
|
|
||||||
const dfs = (nodeId, group) => {
|
|
||||||
visited.add(nodeId);
|
|
||||||
group.push(nodeMap[nodeId]);
|
|
||||||
graph[nodeId].forEach((nei) => {
|
|
||||||
if (!visited.has(nei)) dfs(nei, group);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
nodes.forEach((n) => {
|
|
||||||
if (!visited.has(n.id)) {
|
|
||||||
const group = [];
|
|
||||||
dfs(n.id, group);
|
|
||||||
result.push(group);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FlowDetail;
|
export default FlowDetail;
|
||||||
|
|
@ -30,6 +30,7 @@ const EditNode = forwardRef(({ onFinish }, ref) => {
|
||||||
|
|
||||||
return <ModalForm
|
return <ModalForm
|
||||||
title="编辑"
|
title="编辑"
|
||||||
|
trigger={<></>}
|
||||||
open={open}
|
open={open}
|
||||||
onOpenChange={e => setOpen(e)}
|
onOpenChange={e => setOpen(e)}
|
||||||
formRef={formRef}
|
formRef={formRef}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue