Skip to content

Commit 2b3a56c

Browse files
committed
add flow detail
1 parent e161c91 commit 2b3a56c

File tree

12 files changed

+625
-6
lines changed

12 files changed

+625
-6
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@use "@/config/variables" as *;
2+
3+
.descriptions-list {
4+
padding: 3px;
5+
background-color: white;
6+
7+
.descriptions-list-item {
8+
background-color: white;
9+
padding: 15px;
10+
font-size: $content-font-size;
11+
display: flex;
12+
align-items: center;
13+
justify-content: space-between;
14+
border-bottom: 1px solid $body-background-color;
15+
16+
.descriptions-list-item-label {
17+
font-weight: bold;
18+
}
19+
20+
.descriptions-list-item-value {
21+
}
22+
}
23+
24+
.form-header-title {
25+
width: 100%;
26+
height: 20px;
27+
border-left: 4px solid $theme-primary-color;
28+
padding-left: 4px;
29+
font-size: 14px;
30+
margin-bottom: 10px;
31+
}
32+
}
33+
34+
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import React, {useEffect, useImperativeHandle} from "react";
2+
import {FormField} from "@/components/form/types";
3+
import "./index.scss";
4+
5+
export interface DescriptionsAction {
6+
// 重新刷新数据
7+
reload: () => void;
8+
}
9+
10+
// 详情展示属性
11+
interface DescriptionsProps {
12+
// 展示的字段
13+
columns?: FormField[];
14+
// 请求数据
15+
request?: () => Promise<any>;
16+
// 操作对象
17+
actionRef?: React.Ref<DescriptionsAction>;
18+
// 页脚
19+
footer?: React.ReactNode;
20+
// 页头
21+
header?: React.ReactNode;
22+
// 数据转换
23+
dataConvert?: (field: FormField, data: any) => Promise<any>;
24+
}
25+
26+
// 详情展示
27+
const Descriptions: React.FC<DescriptionsProps> = (props) => {
28+
29+
const [data, setData] = React.useState<any>(null);
30+
31+
const reload = () => {
32+
if (props.request) {
33+
props.request().then((data) => {
34+
if(props.dataConvert) {
35+
const promise = [] as Promise<any>[];
36+
props.columns?.map(item => {
37+
promise.push(new Promise<any>((resolve, reject) => {
38+
props.dataConvert?.(item, data).then(value => {
39+
data[item.props.name] = value;
40+
resolve(value);
41+
}).catch(reject);
42+
}));
43+
});
44+
Promise.all(promise).then(() => {
45+
setData(data);
46+
});
47+
}else {
48+
setData(data);
49+
}
50+
})
51+
}
52+
}
53+
54+
useImperativeHandle(props.actionRef, () => ({
55+
reload: () => {
56+
reload()
57+
}
58+
}), [props.actionRef]);
59+
60+
useEffect(() => {
61+
reload();
62+
63+
return () => {
64+
setData(null);
65+
}
66+
}, []);
67+
68+
return (
69+
<div className={"descriptions-list"}>
70+
{props.header}
71+
{data && props.columns && props.columns
72+
.filter(item => !item.props.hidden)
73+
.map((item) => {
74+
const key = item.props.name;
75+
const label = item.props.label as string || "";
76+
const value = data[key];
77+
const valueType = typeof value === 'object' ? 'object' : 'string';
78+
return (
79+
<div className={"descriptions-list-item"}>
80+
<div className={"descriptions-list-item-label"}
81+
dangerouslySetInnerHTML={{__html: label }}/>
82+
{valueType === 'string' && (
83+
<div className={"descriptions-list-item-value"}
84+
dangerouslySetInnerHTML={{__html: value}}/>
85+
)}
86+
{valueType === 'object' && (
87+
<div className={"descriptions-list-item-value"}>
88+
{value}
89+
</div>
90+
)}
91+
</div>
92+
)
93+
})}
94+
{props.footer}
95+
</div>
96+
)
97+
}
98+
99+
export default Descriptions;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import React, {useContext, useEffect} from "react";
2+
import '@logicflow/core/es/index.css';
3+
import '@logicflow/extension/lib/style/index.css';
4+
import {LogicFlow, Options} from "@logicflow/core";
5+
import {DndPanel, Menu, MiniMap, Snapshot} from "@logicflow/extension";
6+
import {FlowViewReactContext} from "@/components/flow/view";
7+
import Start from "@/components/flow/nodes/Start";
8+
import Over from "@/components/flow/nodes/Over";
9+
import Circulate from "@/components/flow/nodes/Circulate";
10+
import Node from "@/components/flow/nodes/Node";
11+
import "@/components/flow/index.scss";
12+
13+
import EdgeType = Options.EdgeType;
14+
15+
interface FlowChartProps {
16+
edgeType?: EdgeType;
17+
}
18+
19+
const FlowChart: React.FC<FlowChartProps> = (props) => {
20+
21+
const flowViewReactContext = useContext(FlowViewReactContext);
22+
const flowRecordContext = flowViewReactContext?.flowRecordContext;
23+
const flowSchema = flowRecordContext?.getFlowSchema();
24+
25+
const [url, setUrl] = React.useState<string>('');
26+
27+
const edgeType = props.edgeType || 'polyline';
28+
const container = React.useRef<HTMLDivElement>(null);
29+
const lfRef = React.useRef<LogicFlow>(null);
30+
31+
useEffect(() => {
32+
const SilentConfig = {
33+
isSilentMode: true,
34+
stopScrollGraph: false,
35+
stopMoveGraph: false,
36+
stopZoomGraph: false,
37+
edgeTextEdit: false,
38+
};
39+
40+
//@ts-ignore
41+
lfRef.current = new LogicFlow({
42+
//@ts-ignore
43+
container: container.current,
44+
...SilentConfig,
45+
background: {
46+
backgroundColor: '#f3f5f8'
47+
},
48+
width: 0,
49+
height: 0,
50+
plugins: [Menu, DndPanel, MiniMap, Snapshot],
51+
grid: false,
52+
edgeType: edgeType,
53+
});
54+
55+
lfRef.current.setTheme({
56+
bezier: {
57+
stroke: '#8f94e3',
58+
strokeWidth: 1,
59+
},
60+
polyline: {
61+
stroke: '#8f94e3',
62+
strokeWidth: 1,
63+
},
64+
line: {
65+
stroke: '#8f94e3',
66+
strokeWidth: 1,
67+
},
68+
});
69+
lfRef.current.register(Start);
70+
lfRef.current.register(Node);
71+
lfRef.current.register(Over);
72+
lfRef.current.register(Circulate);
73+
lfRef.current.render(flowSchema);
74+
75+
setTimeout(() => {
76+
lfRef.current?.getSnapshotBlob().then((blob: any) => {
77+
setUrl(URL.createObjectURL(blob.data));
78+
});
79+
}, 100)
80+
81+
}, [flowViewReactContext]);
82+
83+
return (
84+
<div>
85+
<div className="flow-chart">
86+
<div className={"flow-chart-content"} ref={container}/>
87+
{url && (
88+
<img src={url} className={"flow-img"}/>
89+
)}
90+
</div>
91+
</div>
92+
)
93+
}
94+
95+
export default FlowChart;

admin-pro-ui/src/components/flow/components/FlowContent.tsx

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ import {FlowFormViewProps} from "@/components/flow/types";
33
import {FlowViewReactContext} from "@/components/flow/view";
44
import {useSelector} from "react-redux";
55
import {FlowReduxState} from "@/components/flow/store/FlowSlice";
6-
import {Tabs, TabsProps} from "antd";
6+
import {Divider, Tabs, TabsProps} from "antd";
7+
import FlowFormOpinion from "@/components/flow/components/FlowFormOpinion";
8+
import FlowHistory from "@/components/flow/components/FlowHistory";
9+
import FlowOpinion from "@/components/flow/components/FlowOpinion";
10+
import FlowChart from "@/components/flow/components/FlowChart";
11+
import FlowHistoryLine from "@/components/flow/components/FlowHistoryLine";
712

813
const FlowContent = () => {
914
const flowViewReactContext = useContext(FlowViewReactContext);
@@ -18,6 +23,7 @@ const FlowContent = () => {
1823
const opinionVisible = useSelector((state: FlowReduxState) => state.flow.opinionVisible);
1924
const dataVersion = useSelector((state: FlowReduxState) => state.flow.dataVersion);
2025
const contentHiddenVisible = useSelector((state: FlowReduxState) => state.flow.contentHiddenVisible);
26+
const [currentTab, setCurrentTab] = React.useState('detail');
2127

2228
useEffect(() => {
2329
if (!flowRecordContext?.isEditable()) {
@@ -46,7 +52,44 @@ const FlowContent = () => {
4652

4753
return (
4854
<div className={"flow-view-content"} style={style}>
49-
<Tabs items={items}/>
55+
<Tabs
56+
items={items}
57+
activeKey={currentTab}
58+
onChange={(value) => {
59+
setCurrentTab(value)
60+
}}/>
61+
62+
{currentTab ==='detail' && (
63+
<>
64+
{formInstance && (
65+
<FlowFormView
66+
data={formParams}
67+
form={formInstance}
68+
dataVersion={dataVersion}
69+
/>
70+
)}
71+
72+
{opinionVisible && (
73+
<FlowFormOpinion/>
74+
)}
75+
</>
76+
)}
77+
78+
{currentTab ==='record' && (
79+
<>
80+
<FlowHistory/>
81+
<Divider>审批记录</Divider>
82+
<FlowOpinion/>
83+
</>
84+
)}
85+
86+
{currentTab ==='chart' && (
87+
<>
88+
<FlowChart/>
89+
<Divider>流转历史</Divider>
90+
<FlowHistoryLine/>
91+
</>
92+
)}
5093
</div>
5194
)
5295
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React, {useContext, useEffect} from "react";
2+
import Form from "@/components/form";
3+
import {FlowViewReactContext} from "@/components/flow/view";
4+
5+
const FlowFormOpinion = ()=>{
6+
7+
const flowViewReactContext = useContext(FlowViewReactContext);
8+
const opinionInstance = flowViewReactContext?.opinionInstance;
9+
const flowRecordContext = flowViewReactContext?.flowRecordContext;
10+
11+
useEffect(() => {
12+
opinionInstance?.setFieldValue("advice", flowRecordContext?.getOpinionAdvice());
13+
}, []);
14+
15+
return (
16+
<>
17+
<Form
18+
form={opinionInstance}
19+
loadFields={async ()=>{
20+
return [
21+
{
22+
type:'textarea',
23+
props:{
24+
name:"advice",
25+
label:"审批意见",
26+
textAreaRows:2,
27+
required:true,
28+
validateFunction:async (content)=>{
29+
const value = content.value;
30+
if(value){
31+
return [];
32+
}
33+
return ["请输入审批意见"];
34+
}
35+
}
36+
}
37+
]
38+
}}
39+
>
40+
</Form>
41+
</>
42+
)
43+
}
44+
45+
export default FlowFormOpinion;

0 commit comments

Comments
 (0)