4 Commits

Author SHA1 Message Date
03d0d9d85b feat(web): 尝试优化流程图性能 2025-07-06 22:39:26 +08:00
187c565da4 feat(ai-web): 将任务模板信息固化到任务对象中
保证每个任务对象都有对应的模板信息可以追溯,不会随着模板被修改而失效
2025-07-05 18:45:24 +08:00
f3dfff5075 fix(web): 修复删除api的路径 2025-07-05 17:41:27 +08:00
69420094ec feat(web): 实现任务显示文件列表 2025-07-05 17:39:44 +08:00
17 changed files with 221 additions and 92 deletions

View File

@@ -38,11 +38,15 @@ create table hudi_collect_build_b12.service_ai_flow_task
id bigint not null comment '记录唯一标记', id bigint not null comment '记录唯一标记',
created_time datetime(6) comment '记录创建时间', created_time datetime(6) comment '记录创建时间',
modified_time datetime(6) comment '记录更新时间', modified_time datetime(6) comment '记录更新时间',
comment text comment '任务注释,用于额外说明',
error longtext comment '任务运行产生的报错', error longtext comment '任务运行产生的报错',
input longtext comment '任务输入', input longtext comment '任务输入',
result longtext comment '任务运行结果', result longtext comment '任务运行结果',
status enum ('ERROR','FINISHED','RUNNING') not null comment '任务运行状态', status enum ('ERROR','FINISHED','RUNNING') not null comment '任务运行状态',
template_id bigint not null comment '流程任务对应的模板', template_description varchar(255) comment '任务对应的模板功能、内容说明',
template_flow_graph longtext not null comment '任务对应的模板前端流程图数据',
template_input_schema longtext not null comment '任务对应的模板入参Schema',
template_name varchar(255) not null comment '任务对应的模板名称',
primary key (id) primary key (id)
) comment ='流程任务记录' charset = utf8mb4; ) comment ='流程任务记录' charset = utf8mb4;

View File

@@ -21,7 +21,10 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.collections.api.factory.Sets;
import org.eclipse.collections.api.list.ImmutableList; import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.set.ImmutableSet;
import org.mapstruct.factory.Mappers;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -56,6 +59,26 @@ public class DataFileController {
this.sliceFolderPath = StrUtil.format("{}/slice", uploadFolderPath); this.sliceFolderPath = StrUtil.format("{}/slice", uploadFolderPath);
} }
@PostMapping("/detail")
public AmisResponse<?> detail(@RequestBody DetailRequest request) {
var mapper = Mappers.getMapper(DetailResponse.Mapper.class);
return AmisResponse.responseCrudData(dataFileService.downloadFile(request.ids).collect(mapper::from));
}
@GetMapping("/detail")
public AmisResponse<?> detail(@RequestParam("ids") String ids) {
if (StrUtil.isBlank(ids)) {
return AmisResponse.responseCrudData(Sets.immutable.empty());
}
var mapper = Mappers.getMapper(DetailResponse.Mapper.class);
return AmisResponse.responseCrudData(
dataFileService.downloadFile(
Sets.immutable.ofAll(StrUtil.split(ids, ","))
.collect(Long::parseLong)
).collect(mapper::from)
);
}
@PostMapping("") @PostMapping("")
public AmisResponse<FinishResponse> upload(@RequestParam("file") MultipartFile file) throws IOException { public AmisResponse<FinishResponse> upload(@RequestParam("file") MultipartFile file) throws IOException {
String filename = file.getOriginalFilename(); String filename = file.getOriginalFilename();
@@ -170,6 +193,24 @@ public class DataFileController {
} }
} }
@Data
public static final class DetailRequest {
private ImmutableSet<Long> ids;
}
@Data
public static final class DetailResponse {
private Long id;
private String filename;
private Long size;
private String md5;
@org.mapstruct.Mapper
public interface Mapper {
DetailResponse from(DataFile file);
}
}
@Data @Data
public static final class StartRequest { public static final class StartRequest {
private String name; private String name;

View File

@@ -13,7 +13,6 @@ import java.util.Map;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -37,17 +36,26 @@ public class TaskController extends SimpleControllerSupport<FlowTask, TaskContro
} }
@GetMapping("input_data/{id}") @GetMapping("input_data/{id}")
public AmisResponse<?> getInputSchema(@PathVariable("id") Long id) throws JsonProcessingException { public AmisResponse<?> getInputData(@PathVariable("id") Long id) throws JsonProcessingException {
var task = flowTaskService.detailOrThrow(id); var task = flowTaskService.detailOrThrow(id);
return AmisResponse.responseSuccess(mapper.readValue(task.getInput(), Map.class)); return AmisResponse.responseSuccess(mapper.readValue(task.getInput(), Map.class));
} }
@GetMapping("input_schema/{id}")
public AmisResponse<?> getInputSchema(@PathVariable("id") Long id) throws JsonProcessingException {
var task = flowTaskService.detailOrThrow(id);
return AmisResponse.responseSuccess(mapper.readValue(task.getTemplateInputSchema(), Map.class));
}
@Override @Override
protected SaveItemMapper<FlowTask, SaveItem> saveItemMapper() { protected SaveItemMapper<FlowTask, SaveItem> saveItemMapper() {
return item -> { return item -> {
FlowTask task = new FlowTask(); FlowTask task = new FlowTask();
FlowTaskTemplate template = flowTaskTemplateService.detailOrThrow(item.getTemplateId()); FlowTaskTemplate template = flowTaskTemplateService.detailOrThrow(item.getTemplateId());
task.setTemplate(template); task.setTemplateName(template.getName());
task.setTemplateDescription(template.getDescription());
task.setTemplateInputSchema(template.getInputSchema());
task.setTemplateFlowGraph(template.getFlowGraph());
task.setInput(mapper.writeValueAsString(item.getInput())); task.setInput(mapper.writeValueAsString(item.getInput()));
return task; return task;
}; };
@@ -74,14 +82,11 @@ public class TaskController extends SimpleControllerSupport<FlowTask, TaskContro
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public static class ListItem extends SimpleItem { public static class ListItem extends SimpleItem {
private Long templateId;
private String templateName; private String templateName;
private FlowTask.Status status; private FlowTask.Status status;
@org.mapstruct.Mapper @org.mapstruct.Mapper
public static abstract class Mapper { public static abstract class Mapper {
@Mapping(target = "templateId", source = "task.template.id")
@Mapping(target = "templateName", source = "task.template.name")
public abstract ListItem from(FlowTask task); public abstract ListItem from(FlowTask task);
} }
} }
@@ -94,8 +99,6 @@ public class TaskController extends SimpleControllerSupport<FlowTask, TaskContro
@org.mapstruct.Mapper @org.mapstruct.Mapper
public static abstract class Mapper extends ListItem.Mapper { public static abstract class Mapper extends ListItem.Mapper {
@Mapping(target = "templateId", source = "task.template.id")
@Mapping(target = "templateName", source = "task.template.name")
public abstract DetailItem from(FlowTask task); public abstract DetailItem from(FlowTask task);
} }
} }

View File

@@ -3,14 +3,10 @@ package com.lanyuanxiaoyao.service.ai.web.entity;
import com.lanyuanxiaoyao.service.ai.web.base.entity.SimpleEntity; import com.lanyuanxiaoyao.service.ai.web.base.entity.SimpleEntity;
import com.lanyuanxiaoyao.service.common.Constants; import com.lanyuanxiaoyao.service.common.Constants;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners; import jakarta.persistence.EntityListeners;
import jakarta.persistence.EnumType; import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated; import jakarta.persistence.Enumerated;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -28,10 +24,23 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@Table(catalog = Constants.DATABASE_NAME, name = "service_ai_flow_task") @Table(catalog = Constants.DATABASE_NAME, name = "service_ai_flow_task")
@Comment("流程任务记录") @Comment("流程任务记录")
public class FlowTask extends SimpleEntity { public class FlowTask extends SimpleEntity {
@Comment("流程任务对应的模板") // 每个任务对应的模板都是唯一,避免模板修改之后任务的状态、运行等状态都无法展示
@ManyToOne // 不管允许不允许任务重跑,这些都要保存下来
@JoinColumn(nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) @Comment("任务对应的模板名称")
private FlowTaskTemplate template; @Column(nullable = false)
private String templateName;
@Comment("任务对应的模板功能、内容说明")
private String templateDescription;
@Comment("任务对应的模板入参Schema")
@Column(nullable = false, columnDefinition = "longtext")
private String templateInputSchema;
@Comment("任务对应的模板前端流程图数据")
@Column(nullable = false, columnDefinition = "longtext")
private String templateFlowGraph = "{}";
@Comment("任务注释,用于额外说明")
@Column(columnDefinition = "text")
private String comment;
@Comment("任务输入") @Comment("任务输入")
@Column(columnDefinition = "longtext") @Column(columnDefinition = "longtext")
private String input; private String input;

View File

@@ -25,6 +25,7 @@ import SwitchNode from './node/SwitchNode.tsx'
import {useDataStore} from './store/DataStore.ts' import {useDataStore} from './store/DataStore.ts'
import {useFlowStore} from './store/FlowStore.ts' import {useFlowStore} from './store/FlowStore.ts'
import {useNavigate} from 'react-router' import {useNavigate} from 'react-router'
import {useShallow} from 'zustand/react/shallow'
const FlowableDiv = styled.div` const FlowableDiv = styled.div`
height: 100%; height: 100%;
@@ -66,6 +67,7 @@ const FlowableDiv = styled.div`
export type GraphData = { nodes: Node[], edges: Edge[], data: any } export type GraphData = { nodes: Node[], edges: Edge[], data: any }
export type FlowEditorProps = { export type FlowEditorProps = {
// inputSchema: Record<string, Record<string, any>>,
graphData: GraphData, graphData: GraphData,
onGraphDataChange: (graphData: GraphData) => void, onGraphDataChange: (graphData: GraphData) => void,
} }
@@ -106,18 +108,38 @@ function FlowEditor(props: FlowEditorProps) {
]) ])
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const {data, setData, getDataById, setDataById} = useDataStore() const {data, setData, getDataById, setDataById} = useDataStore(
useShallow(state => ({
data: state.data,
setData: state.setData,
getDataById: state.getDataById,
setDataById: state.setDataById,
}))
)
const { const {
nodes, nodes,
addNode, addNode,
removeNode,
setNodes, setNodes,
onNodesChange, onNodesChange,
edges, edges,
setEdges, setEdges,
onEdgesChange, onEdgesChange,
onConnect, onConnect,
} = useFlowStore() } = useFlowStore(
useShallow(state => ({
nodes: state.nodes,
getNodes: state.getNodes,
addNode: state.addNode,
removeNode: state.removeNode,
setNodes: state.setNodes,
onNodesChange: state.onNodesChange,
edges: state.edges,
getEdges: state.getEdges,
setEdges: state.setEdges,
onEdgesChange: state.onEdgesChange,
onConnect: state.onConnect,
}))
)
const [currentNodeForm, setCurrentNodeForm] = useState<JSX.Element>() const [currentNodeForm, setCurrentNodeForm] = useState<JSX.Element>()
const editNode = (id: string, columnSchema?: Schema[]) => { const editNode = (id: string, columnSchema?: Schema[]) => {
@@ -197,10 +219,8 @@ function FlowEditor(props: FlowEditorProps) {
// 用于透传node操作到主流程 // 用于透传node操作到主流程
const initialNodeHandlers = { const initialNodeHandlers = {
getDataById, // getInputSchema: () => props.inputSchema,
setDataById, editNode
removeNode,
editNode,
} }
useEffect(() => { useEffect(() => {

View File

@@ -1,27 +1,36 @@
import {DeleteFilled, EditFilled} from '@ant-design/icons' import {
import {Handle, type HandleProps, type NodeProps, Position, useNodeConnections} from '@xyflow/react' type Edge,
Handle,
type HandleProps,
type Node,
type NodeProps,
Position,
useNodeConnections
} from '@xyflow/react'
import type {Schema} from 'amis' import type {Schema} from 'amis'
import {Card, Dropdown} from 'antd' import {Button, Card} from 'antd'
import {isEmpty, isEqual, isNil} from 'licia' import {isEmpty, isEqual, isNil} from 'licia'
import {type JSX} from 'react' import {type JSX, useMemo} from 'react'
import {horizontalFormOptions} from '../../../util/amis.tsx' import {horizontalFormOptions} from '../../../util/amis.tsx'
import {useDataStore} from '../store/DataStore.ts'
import {EditFilled} from '@ant-design/icons'
export type AmisNodeType = 'normal' | 'start' | 'end' export type AmisNodeType = 'normal' | 'start' | 'end'
export function inputsFormColumns(required: boolean = false, preload?: any): Schema[] { export function inputsFormColumns(props: ColumnsSchemaProps): Schema[] {
console.log('props', props)
return [ return [
{ {
type: 'input-kvs', type: 'input-kvs',
name: 'inputs', name: 'inputs',
label: '输入变量', label: '输入变量',
value: preload,
addButtonText: '新增输入', addButtonText: '新增输入',
draggable: false, draggable: false,
keyItem: { keyItem: {
...horizontalFormOptions(), ...horizontalFormOptions(),
label: '参数名称', label: '参数名称',
}, },
required: required,
valueItems: [ valueItems: [
{ {
...horizontalFormOptions(), ...horizontalFormOptions(),
@@ -95,6 +104,14 @@ export const LimitHandler = (props: HandleProps & { limit: number }) => {
) )
} }
type ColumnsSchemaProps = {
// getInputSchema: () => Record<string, Record<string, any>>,
nodeId: string,
nodeData: any,
getNodes: () => Node[],
getEdges: () => Edge[],
}
type AmisNodeProps = { type AmisNodeProps = {
nodeProps: NodeProps nodeProps: NodeProps
type: AmisNodeType type: AmisNodeType
@@ -102,7 +119,7 @@ type AmisNodeProps = {
defaultNodeDescription?: String defaultNodeDescription?: String
extraNodeDescription?: (nodeData: any) => JSX.Element extraNodeDescription?: (nodeData: any) => JSX.Element
handlers?: (nodeData: any) => JSX.Element handlers?: (nodeData: any) => JSX.Element
columnSchema?: Schema[] columnSchema?: (props: ColumnsSchemaProps) => Schema[]
} }
const AmisNode: (props: AmisNodeProps) => JSX.Element = ({ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
@@ -114,16 +131,24 @@ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
handlers, handlers,
columnSchema, columnSchema,
}) => { }) => {
// const {
// removeNode,
// getNodes,
// getEdges
// } = useFlowStore()
const {getDataById} = useDataStore()
const {id, data} = nodeProps const {id, data} = nodeProps
const {getDataById, removeNode, editNode} = data const {editNode} = data
// @ts-ignore // @ts-ignore
const nodeData = getDataById(id) const nodeData = useMemo(() => {
return getDataById(id)
}, [id])
const nodeName = isEmpty(nodeData?.node?.name) ? defaultNodeName : nodeData.node.name const nodeName = isEmpty(nodeData?.node?.name) ? defaultNodeName : nodeData.node.name
const nodeDescription = isEmpty(nodeData?.node?.description) ? defaultNodeDescription : nodeData.node?.description const nodeDescription = isEmpty(nodeData?.node?.description) ? defaultNodeDescription : nodeData.node?.description
return ( return (
<div className="w-64"> <div className="w-64">
<Dropdown {/* className="card-container"
className="card-container"
trigger={['contextMenu']} trigger={['contextMenu']}
menu={{ menu={{
items: [ items: [
@@ -160,7 +185,18 @@ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
{ {
type: 'divider', type: 'divider',
}, },
...(columnSchema ?? []), ...(
columnSchema?.({
// @ts-ignore
// getInputSchema,
nodeId: id,
nodeData,
// @ts-ignore
getNodes,
// @ts-ignore
getEdges,
}) ?? []
),
], ],
) )
break break
@@ -171,7 +207,7 @@ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
} }
}, },
}} }}
> >*/}
<Card <Card
className="node-card" className="node-card"
title={nodeName} title={nodeName}
@@ -183,7 +219,6 @@ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
{extraNodeDescription?.(nodeData)} {extraNodeDescription?.(nodeData)}
</div> </div>
</Card> </Card>
</Dropdown>
{isNil(handlers) {isNil(handlers)
? <> ? <>
{isEqual(type, 'start') || isEqual(type, 'normal') {isEqual(type, 'start') || isEqual(type, 'normal')

View File

@@ -7,8 +7,8 @@ const CodeNode = (props: NodeProps) => AmisNode({
type: 'normal', type: 'normal',
defaultNodeName: '代码执行', defaultNodeName: '代码执行',
defaultNodeDescription: '执行自定义的处理代码', defaultNodeDescription: '执行自定义的处理代码',
columnSchema: [ columnSchema: (props) => [
...inputsFormColumns(), ...inputsFormColumns(props),
{ {
type: 'divider', type: 'divider',
}, },

View File

@@ -8,8 +8,8 @@ const KnowledgeNode = (props: NodeProps) => AmisNode({
type: 'normal', type: 'normal',
defaultNodeName: '知识库', defaultNodeName: '知识库',
defaultNodeDescription: '查询知识库获取外部知识', defaultNodeDescription: '查询知识库获取外部知识',
columnSchema: [ columnSchema: (props) => [
...inputsFormColumns(), ...inputsFormColumns(props),
{ {
type: 'divider', type: 'divider',
}, },

View File

@@ -22,8 +22,8 @@ const LlmNode = (props: NodeProps) => AmisNode({
</div> </div>
: <></> : <></>
}, },
columnSchema: [ columnSchema: (props) => [
...inputsFormColumns(), ...inputsFormColumns(props),
{ {
type: 'divider', type: 'divider',
}, },

View File

@@ -7,7 +7,7 @@ const OutputNode = (props: NodeProps) => AmisNode({
type: 'end', type: 'end',
defaultNodeName: '输出节点', defaultNodeName: '输出节点',
defaultNodeDescription: '定义输出变量', defaultNodeDescription: '定义输出变量',
columnSchema: outputsFormColumns(true), columnSchema: () => outputsFormColumns(true),
}) })
export default React.memo(OutputNode) export default React.memo(OutputNode)

View File

@@ -20,7 +20,7 @@ const SwitchNode = (props: NodeProps) => AmisNode({
type: 'normal', type: 'normal',
defaultNodeName: '分支节点', defaultNodeName: '分支节点',
defaultNodeDescription: '根据不同的情况前往不同的分支', defaultNodeDescription: '根据不同的情况前往不同的分支',
columnSchema: [], columnSchema: () => [],
// @ts-ignore // @ts-ignore
extraNodeDescription: nodeData => { extraNodeDescription: nodeData => {
return ( return (

View File

@@ -1,12 +1,14 @@
import {create} from 'zustand/react' import {create} from 'zustand/react'
export const useDataStore = create<{ export type DataStoreState = {
data: Record<string, any>, data: Record<string, any>,
getData: () => Record<string, any>, getData: () => Record<string, any>,
setData: (data: Record<string, any>) => void, setData: (data: Record<string, any>) => void,
getDataById: (id: string) => any, getDataById: (id: string) => any,
setDataById: (id: string, data: any) => void, setDataById: (id: string, data: any) => void,
}>((set, get) => ({ }
export const useDataStore = create<DataStoreState>((set, get) => ({
data: {}, data: {},
getData: () => get().data, getData: () => get().data,
setData: (data) => set({ setData: (data) => set({

View File

@@ -11,8 +11,9 @@ import {
import {filter, find, isEqual} from 'licia' import {filter, find, isEqual} from 'licia'
import {create} from 'zustand/react' import {create} from 'zustand/react'
export const useFlowStore = create<{ export type FlowStoreState = {
nodes: Node[], nodes: Node[],
getNodes: () => Node[],
onNodesChange: OnNodesChange, onNodesChange: OnNodesChange,
getNodeById: (id: string) => Node | undefined, getNodeById: (id: string) => Node | undefined,
addNode: (node: Node) => void, addNode: (node: Node) => void,
@@ -20,12 +21,16 @@ export const useFlowStore = create<{
setNodes: (nodes: Node[]) => void, setNodes: (nodes: Node[]) => void,
edges: Edge[], edges: Edge[],
getEdges: () => Edge[],
onEdgesChange: OnEdgesChange, onEdgesChange: OnEdgesChange,
setEdges: (edges: Edge[]) => void, setEdges: (edges: Edge[]) => void,
onConnect: OnConnect, onConnect: OnConnect,
}>((set, get) => ({ }
export const useFlowStore = create<FlowStoreState>((set, get) => ({
nodes: [], nodes: [],
getNodes: () => get().nodes,
onNodesChange: changes => { onNodesChange: changes => {
set({ set({
nodes: applyNodeChanges(changes, get().nodes), nodes: applyNodeChanges(changes, get().nodes),
@@ -41,6 +46,7 @@ export const useFlowStore = create<{
setNodes: nodes => set({nodes}), setNodes: nodes => set({nodes}),
edges: [], edges: [],
getEdges: () => get().edges,
onEdgesChange: changes => { onEdgesChange: changes => {
set({ set({
edges: applyEdgeChanges(changes, get().edges), edges: applyEdgeChanges(changes, get().edges),

View File

@@ -101,13 +101,13 @@ const FlowTask: React.FC = () => {
type: 'service', type: 'service',
schemaApi: { schemaApi: {
method: 'get', method: 'get',
url: `${commonInfo.baseAiUrl}/flow_task/template/input_schema/\${templateId}`, url: `${commonInfo.baseAiUrl}/flow_task/input_schema/\${id}`,
// @ts-ignore // @ts-ignore
adaptor: (payload, response, api, context) => { adaptor: (payload, response, api, context) => {
return { return {
...payload, ...payload,
data: { data: {
...generateInputForm(payload.data, undefined, false), ...generateInputForm(payload.data, undefined, false, true),
id: 'db8a4d10-0c47-4e27-b1a4-d0f2e1c15992', id: 'db8a4d10-0c47-4e27-b1a4-d0f2e1c15992',
initApi: { initApi: {
method: 'get', method: 'get',
@@ -131,12 +131,6 @@ const FlowTask: React.FC = () => {
], ],
}, },
}, },
{
type: 'action',
label: '重新执行',
level: 'link',
size: 'sm',
},
{ {
type: 'action', type: 'action',
label: '删除', label: '删除',
@@ -144,7 +138,7 @@ const FlowTask: React.FC = () => {
level: 'link', level: 'link',
size: 'sm', size: 'sm',
actionType: 'ajax', actionType: 'ajax',
api: `get:${commonInfo.baseAiUrl}/task/remove/\${id}`, api: `get:${commonInfo.baseAiUrl}/flow_task/remove/\${id}`,
confirmText: '确认删除任务记录:${name}', confirmText: '确认删除任务记录:${name}',
confirmTitle: '删除', confirmTitle: '删除',
}, },

View File

@@ -1,5 +1,5 @@
import type {Schema} from 'amis' import type {Schema} from 'amis'
import {commonInfo} from '../../../util/amis.tsx' import {commonInfo, formInputFileStaticColumns} from '../../../util/amis.tsx'
export const typeMap: Record<string, string> = { export const typeMap: Record<string, string> = {
text: '文本', text: '文本',
@@ -13,7 +13,7 @@ export type InputField = {
description?: string description?: string
} }
export const generateInputForm: (inputSchema: Record<string, InputField>, title?: string, border?: boolean, staticView?: boolean) => Schema = (inputSchema, title, border) => { export const generateInputForm: (inputSchema: Record<string, InputField>, title?: string, border?: boolean, staticView?: boolean) => Schema = (inputSchema, title, border, staticView) => {
let items: Schema[] = [] let items: Schema[] = []
for (const name of Object.keys(inputSchema)) { for (const name of Object.keys(inputSchema)) {
let field = inputSchema[name] let field = inputSchema[name]
@@ -34,6 +34,17 @@ export const generateInputForm: (inputSchema: Record<string, InputField>, title?
commonMeta.type = 'input-number' commonMeta.type = 'input-number'
break break
case 'files': case 'files':
if (staticView) {
items.push({
...commonMeta,
type: 'control',
body: {
type: 'crud',
api: `${commonInfo.baseAiUrl}/upload/detail?ids=\${JOIN(inputData.${name}, ',')}`,
columns: formInputFileStaticColumns,
}
})
} else {
items.push({ items.push({
...commonMeta, ...commonMeta,
type: 'input-file', type: 'input-file',
@@ -46,6 +57,7 @@ export const generateInputForm: (inputSchema: Record<string, InputField>, title?
maxSize: 104857600, maxSize: 104857600,
receiver: `${commonInfo.baseAiUrl}/upload`, receiver: `${commonInfo.baseAiUrl}/upload`,
}) })
}
break break
} }
} }

View File

@@ -12,6 +12,7 @@ const FlowTaskTemplateFlowEditDiv = styled.div`
const FlowTaskTemplateFlowEdit: React.FC = () => { const FlowTaskTemplateFlowEdit: React.FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
const {template_id} = useParams() const {template_id} = useParams()
// const [inputSchema, setInputSchema] = useState<Record<string, Record<string, any>>>({})
const [graphData, setGraphData] = useState<GraphData>({nodes: [], edges: [], data: {}}) const [graphData, setGraphData] = useState<GraphData>({nodes: [], edges: [], data: {}})
useMount(async () => { useMount(async () => {
@@ -21,12 +22,14 @@ const FlowTaskTemplateFlowEdit: React.FC = () => {
headers: commonInfo.authorizationHeaders headers: commonInfo.authorizationHeaders
} }
) )
// setInputSchema(data?.data?.inputSchema)
setGraphData(data?.data?.flowGraph) setGraphData(data?.data?.flowGraph)
}) })
return ( return (
<FlowTaskTemplateFlowEditDiv className="h-full w-full"> <FlowTaskTemplateFlowEditDiv className="h-full w-full">
<FlowEditor <FlowEditor
// inputSchema={inputSchema}
graphData={graphData} graphData={graphData}
onGraphDataChange={async data => { onGraphDataChange={async data => {
await axios.post( await axios.post(

View File

@@ -2519,7 +2519,7 @@ export function pictureFromIds(field: string) {
return `\${ARRAYMAP(${field},id => '${commonInfo.baseAiUrl}/upload/download/' + id)}` return `\${ARRAYMAP(${field},id => '${commonInfo.baseAiUrl}/upload/download/' + id)}`
} }
const formInputFileStaticColumns = [ export const formInputFileStaticColumns = [
{ {
name: 'filename', name: 'filename',
label: '文件名', label: '文件名',