feat(web): workflow app logs

This commit is contained in:
zhaoying
2026-04-24 18:05:01 +08:00
parent c7c1570d40
commit 32dfee803a
5 changed files with 80 additions and 31 deletions

View File

@@ -7,7 +7,7 @@
import { type FC, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { Flex, Button } from 'antd';
import { Flex, Button, Form } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { getAppLogsUrl } from '@/api/application';
@@ -15,11 +15,14 @@ import Table from '@/components/Table'
import { formatDateTime } from '@/utils/format';
import type { LogItem, LogDetailModalRef } from './types'
import LogDetailModal from './components/LogDetailModal'
import SearchInput from '@/components/SearchInput'
const Statistics: FC = () => {
const { t } = useTranslation();
const { id } = useParams();
const logDetailRef = useRef<LogDetailModalRef>(null);
const [form] = Form.useForm();
const values = Form.useWatch([], form);
const handleViewDetail = (item: LogItem) => {
logDetailRef.current?.handleOpen(item);
@@ -62,15 +65,26 @@ const Statistics: FC = () => {
];
return (
<div className="rb:bg-white rb:rounded-lg rb:pt-3 rb:px-3">
<Flex justify="flex-end" className="rb:mb-3!">
<Form form={form}>
<Form.Item name="keyword" noStyle>
<SearchInput
placeholder={t('application.logSearchPlaceholder')}
variant="outlined"
/>
</Form.Item>
</Form>
</Flex>
<Table<LogItem>
apiUrl={getAppLogsUrl(id || '')}
apiParams={{
is_draft: false,
...(values ?? {})
}}
columns={columns}
rowKey="id"
isScroll={true}
scrollY="calc(100vh - 214px)"
scrollY="calc(100vh - 242px)"
/>
<LogDetailModal ref={logDetailRef} />
</div>

View File

@@ -1,8 +1,8 @@
/*
* @Author: ZhaoYing
* @Date: 2026-03-24 16:31:24
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-24 16:31:24
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-04-24 17:49:58
*/
import { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
import { Flex, Button, Empty, Skeleton } from 'antd';
@@ -14,6 +14,12 @@ import { getAppLogDetail } from '@/api/application'
import ChatContent from '@/components/Chat/ChatContent'
import { formatDateTime } from '@/utils/format'
import type { ChatItem } from '@/components/Chat/types'
import Runtime from '@/views/Workflow/components/Chat/Runtime'
import { nodeLibrary } from '@/views/Workflow/constant'
const nodeIconMap = Object.fromEntries(
nodeLibrary.flatMap(c => c.nodes.map(n => [n.type, n.icon]))
)
/** Log detail data with conversation messages */
type Data = LogItem & {
@@ -54,7 +60,30 @@ const LogDetailModal = forwardRef<LogDetailModalRef>((_props, ref) => {
if (!vo) return
setLoading(true)
getAppLogDetail(vo.app_id, vo.id).then(res => {
setData(res as Data)
const { node_executions_map, messages, ...rest } = res as Data;
let hasSubContentMessages = messages
if (messages && messages.length > 0 && node_executions_map && Object.keys(node_executions_map).length > 0) {
hasSubContentMessages = messages.map(item => {
if (item.id && node_executions_map[item.id]) {
item.subContent = node_executions_map[item.id]?.map(({ input, output, cycle_items = [], error, process, ...node }: any) => {
const converted: any = { ...node, icon: nodeIconMap[node.node_type], content: { input, output, process, error } }
if (node.node_type === 'loop' && Array.isArray(cycle_items) && cycle_items.length > 0) {
converted.subContent = cycle_items.map(({ input: cInput, output: cOutput, error: cError, process: cProcess, ...cNode }: any) => ({
...cNode,
icon: nodeIconMap[cNode.node_type],
content: { input: cInput, output: cOutput, process: cProcess, error: cError }
}))
}
return converted
})
}
return { ...item }
})
}
setData({
...rest,
messages: hasSubContentMessages
})
})
.finally(() => {
setLoading(false)
@@ -66,6 +95,8 @@ const LogDetailModal = forwardRef<LogDetailModalRef>((_props, ref) => {
handleClose
}));
console.log('data', data)
return (
<RbModal
title={<>
@@ -92,6 +123,7 @@ const LogDetailModal = forwardRef<LogDetailModalRef>((_props, ref) => {
data={data.messages || []}
streamLoading={false}
labelFormat={(item) => formatDateTime(item.created_at)}
renderRuntime={(item, index) => <Runtime item={item} index={index} />}
/>
)
}