首页工作空间适配新结构并支持加载失败重试、切换独立删除接口
- 工作空间结果树适配 createDate→flows→items 两级结构(flows 下文件平铺,兼容旧平铺结构) - 删除接口切换:工作空间树删除改走 session 域独立接口 resultDelete(deleteWorkspaceResult),不依赖内容创作域 - 会话记录/工作空间列表加载失败:区分失败态与空态,提供重试按钮;工作空间懒加载失败后切回 tab 自动重试 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -68,16 +68,27 @@ export function deleteSessionRecord(ids: { id: string; type: string }[], request
|
||||
// ===== 工作空间结果列表(/ai-agent/session/resultList)=====
|
||||
// 与内容创作 execution/list 同结构:data.tree(按 createDate 分组)+ data.imgAddressPrefix
|
||||
export interface ExecutionItem {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
// 兼容旧平铺结构;resultList 新结构下 items 无独立 id,删除按流程记录 flow.Id
|
||||
id?: string;
|
||||
timestamp?: string;
|
||||
content: string;
|
||||
label: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
// resultList 新两级结构:createDate → flows(流程分组)→ items(文件)
|
||||
export interface ExecutionFlow {
|
||||
flowName: string;
|
||||
Id: string; // 流程结果记录 id(雪花)
|
||||
sessionId: string;
|
||||
items: ExecutionItem[];
|
||||
}
|
||||
|
||||
export interface ExecutionTreeItem {
|
||||
createDate: string;
|
||||
items: ExecutionItem[];
|
||||
// 新结构:日期下按流程分组;兼容旧平铺结构(无 flows 时回落 items)
|
||||
flows?: ExecutionFlow[];
|
||||
items?: ExecutionItem[];
|
||||
}
|
||||
|
||||
export interface ExecutionListData {
|
||||
@@ -100,3 +111,13 @@ export function getWorkspaceResultList(params?: Record<string, any>, requestOpti
|
||||
requestOptions,
|
||||
}) as Promise<ExecutionListResponse>;
|
||||
}
|
||||
|
||||
/** 删除工作空间结果(单个文件)——工作流域独立接口,不依赖内容创作域 */
|
||||
export function deleteWorkspaceResult(data: { id: string; content: string }, requestOptions?: RequestOptions) {
|
||||
return request({
|
||||
url: '/ai-agent/session/resultDelete',
|
||||
method: 'delete',
|
||||
data,
|
||||
requestOptions,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,12 +47,20 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div v-else-if="historyFailed" class="panel-error">
|
||||
<span class="panel-error-text">加载失败</span>
|
||||
<button class="panel-retry-btn" @click="emit('retry-history')">重试</button>
|
||||
</div>
|
||||
<div v-else class="panel-empty">暂无会话记录</div>
|
||||
</div>
|
||||
|
||||
<!-- 工作空间面板 -->
|
||||
<div v-show="activeTab === 'workspace'" class="panel" v-loading="treeLoading">
|
||||
<el-empty v-if="!treeLoading && treeNodes.length === 0" :image-size="48" description="暂无内容" />
|
||||
<div v-if="!treeLoading && treeNodes.length === 0 && workspaceFailed" class="panel-error">
|
||||
<span class="panel-error-text">加载失败</span>
|
||||
<button class="panel-retry-btn" @click="emit('retry-workspace')">重试</button>
|
||||
</div>
|
||||
<el-empty v-else-if="!treeLoading && treeNodes.length === 0" :image-size="48" description="暂无内容" />
|
||||
<div v-else class="panel-list">
|
||||
<div v-for="dateNode in treeNodes" :key="dateNode.id" class="tree-date-group">
|
||||
<div class="tree-date-label">{{ dateNode.label }}</div>
|
||||
@@ -98,6 +106,11 @@ interface Props {
|
||||
activeMenu: string;
|
||||
activeHistoryId: string | null;
|
||||
historyList: HistoryItem[];
|
||||
// 会话记录 / 工作空间加载失败标记:失败时显示「加载失败 + 重试」错误态
|
||||
historyFailed?: boolean;
|
||||
workspaceFailed?: boolean;
|
||||
// 工作空间是否已成功加载:懒加载成功后才不再触发;失败保持 false → 切回 tab 自动重试
|
||||
workspaceLoaded?: boolean;
|
||||
treeNodes: TreeNode[];
|
||||
treeLoading: boolean;
|
||||
}
|
||||
@@ -111,18 +124,23 @@ interface Emits {
|
||||
(e: 'download-node', data: TreeNode): void;
|
||||
(e: 'delete-node', data: TreeNode): void;
|
||||
(e: 'load-workspace'): void;
|
||||
(e: 'retry-history'): void;
|
||||
(e: 'retry-workspace'): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
historyFailed: false,
|
||||
workspaceFailed: false,
|
||||
workspaceLoaded: false,
|
||||
});
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const activeTab = ref<'history' | 'workspace'>('history');
|
||||
// 工作空间懒加载标记:首次切换到「工作空间」tab 时才通知父组件加载结果树,避免进入页面即拉取
|
||||
const workspaceLoaded = ref(false);
|
||||
const handleTabChange = (tab: 'history' | 'workspace') => {
|
||||
activeTab.value = tab;
|
||||
if (tab === 'workspace' && !workspaceLoaded.value) {
|
||||
workspaceLoaded.value = true;
|
||||
// 懒加载:仅在「工作空间」tab 且尚未成功加载过时通知父组件拉取结果树,避免进入页面即请求;
|
||||
// 失败时父组件不置 workspaceLoaded,切回 tab 自动重试
|
||||
if (tab === 'workspace' && !props.workspaceLoaded) {
|
||||
emit('load-workspace');
|
||||
}
|
||||
};
|
||||
@@ -282,6 +300,34 @@ const handleDeleteNode = (data: TreeNode) => emit('delete-node', data);
|
||||
padding: 32px 0;
|
||||
}
|
||||
|
||||
/* 加载失败错误态:可区分「失败」与「空数据」,并提供重试入口 */
|
||||
.panel-error {
|
||||
padding: 32px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.panel-error-text {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.panel-retry-btn {
|
||||
border: 1px solid #dbeafe;
|
||||
background: #eff6ff;
|
||||
color: #2563eb;
|
||||
font-size: 12px;
|
||||
padding: 4px 14px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
outline: none;
|
||||
|
||||
&:hover {
|
||||
background: #dbeafe;
|
||||
}
|
||||
}
|
||||
|
||||
/* 历史记录条目 */
|
||||
.history-item {
|
||||
display: flex;
|
||||
|
||||
+47
-14
@@ -4,8 +4,11 @@
|
||||
:active-menu="activeMenu"
|
||||
:active-history-id="activeHistoryId"
|
||||
:history-list="historyList"
|
||||
:history-failed="sessionHistoryFailed"
|
||||
:tree-nodes="treeNodes"
|
||||
:tree-loading="treeLoading"
|
||||
:workspace-failed="workspaceTreeFailed"
|
||||
:workspace-loaded="workspaceTreeLoaded"
|
||||
@menu-change="handleMenuChange"
|
||||
@new-chat="handleCreateHistory"
|
||||
@select-history="handleSelectHistory"
|
||||
@@ -14,6 +17,8 @@
|
||||
@download-node="downloadNode"
|
||||
@delete-node="handleDeleteNode"
|
||||
@load-workspace="loadWorkspaceTree"
|
||||
@retry-history="loadSessionHistory"
|
||||
@retry-workspace="loadWorkspaceTree"
|
||||
/>
|
||||
<div class="main-wrapper">
|
||||
<MainContent
|
||||
@@ -101,7 +106,7 @@ import {
|
||||
deleteExecutionResult,
|
||||
downloadToFile,
|
||||
} from '/@/api/settings/creation';
|
||||
import { getSessionListV2, getSessionResults, deleteSessionV2, deleteSessionRecord, getWorkspaceResultList } from '/@/api/settings/workflow/session';
|
||||
import { getSessionListV2, getSessionResults, deleteSessionV2, deleteSessionRecord, getWorkspaceResultList, deleteWorkspaceResult } from '/@/api/settings/workflow/session';
|
||||
import type { ExecutionTreeItem, VOSessionInfoResult } from '/@/api/settings/workflow/session';
|
||||
|
||||
interface HistoryItem {
|
||||
@@ -149,6 +154,11 @@ const activeMenu = ref('chat');
|
||||
const activeHistoryId = ref<string | null>(null);
|
||||
const treeLoading = ref(false);
|
||||
const treeNodes = ref<TreeNode[]>([]);
|
||||
// 会话记录 / 工作空间结果树加载失败标记:失败态可区分、提供重试入口
|
||||
const sessionHistoryFailed = ref(false);
|
||||
const workspaceTreeFailed = ref(false);
|
||||
// 工作空间已成功加载标记:懒加载成功后才不再触发;失败保持 false → 切回 tab 自动重试
|
||||
const workspaceTreeLoaded = ref(false);
|
||||
const imgAddressPrefix = ref('');
|
||||
const previewDialogVisible = ref(false);
|
||||
const previewUrl = ref('');
|
||||
@@ -176,14 +186,32 @@ const buildTreeNodes = (tree: ExecutionTreeItem[]): TreeNode[] =>
|
||||
id: 'date-' + di,
|
||||
label: d.createDate,
|
||||
nodeType: 'date',
|
||||
children: (d.items || []).map((item, ii) => ({
|
||||
id: 'item-' + di + '-' + ii,
|
||||
label: item.label || '作品' + (ii + 1),
|
||||
nodeType: 'title',
|
||||
fileUrl: item.content,
|
||||
fileType: item.type,
|
||||
backendId: item.id,
|
||||
})),
|
||||
children: (() => {
|
||||
// 新结构(createDate → flows → items):flows 下所有文件平铺到日期分组;
|
||||
// 兼容旧平铺结构(createDate → items)
|
||||
const flows = Array.isArray(d.flows) ? d.flows : [];
|
||||
if (flows.length > 0) {
|
||||
return flows.flatMap((flow, fi) =>
|
||||
(flow.items || []).map((item, ii) => ({
|
||||
id: 'item-' + di + '-' + fi + '-' + ii,
|
||||
label: item.label || '作品' + (ii + 1),
|
||||
nodeType: 'title',
|
||||
fileUrl: item.content,
|
||||
fileType: item.type,
|
||||
// 新结构 items 无独立 id,删除按流程结果记录 id(flow.Id)
|
||||
backendId: flow.Id ?? item.id,
|
||||
})),
|
||||
);
|
||||
}
|
||||
return (d.items || []).map((item, ii) => ({
|
||||
id: 'item-' + di + '-' + ii,
|
||||
label: item.label || '作品' + (ii + 1),
|
||||
nodeType: 'title',
|
||||
fileUrl: item.content,
|
||||
fileType: item.type,
|
||||
backendId: item.id,
|
||||
}));
|
||||
})(),
|
||||
}));
|
||||
|
||||
const getPreviewMode = (fileType?: string): 'image' | 'video' | 'audio' | 'iframe' => {
|
||||
@@ -245,10 +273,10 @@ const handleDeleteNode = async (data: TreeNode) => {
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
// 调后端接口删除
|
||||
// 调后端接口删除(工作空间结果:走 session 域独立接口)
|
||||
if (data.backendId && data.fileUrl) {
|
||||
try {
|
||||
await deleteExecutionResult({ id: data.backendId, content: data.fileUrl });
|
||||
await deleteWorkspaceResult({ id: data.backendId, content: data.fileUrl });
|
||||
} catch {
|
||||
// 后端已提示错误,继续本地删除
|
||||
}
|
||||
@@ -288,9 +316,12 @@ const loadWorkspaceTree = async () => {
|
||||
const execRes = await getWorkspaceResultList();
|
||||
imgAddressPrefix.value = execRes.data?.imgAddressPrefix || '';
|
||||
treeNodes.value = buildTreeNodes(execRes.data?.tree || []);
|
||||
workspaceTreeFailed.value = false;
|
||||
// 成功后才标记已加载:失败保持 false,切回「工作空间」tab 时自动重试
|
||||
workspaceTreeLoaded.value = true;
|
||||
} catch {
|
||||
treeNodes.value = [];
|
||||
imgAddressPrefix.value = '';
|
||||
// 失败保留已有树数据(首次为空则显示错误态);不置 workspaceTreeLoaded 以便下次切 tab 重试
|
||||
workspaceTreeFailed.value = true;
|
||||
} finally {
|
||||
treeLoading.value = false;
|
||||
}
|
||||
@@ -325,6 +356,7 @@ const loadSessionHistory = async () => {
|
||||
};
|
||||
});
|
||||
historyList.value = sessions;
|
||||
sessionHistoryFailed.value = false;
|
||||
// 为每个后端会话初始化消息存储
|
||||
sessions.forEach((s) => {
|
||||
if (!sessionMessages.value.has(s.id)) {
|
||||
@@ -332,7 +364,8 @@ const loadSessionHistory = async () => {
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
historyList.value = [];
|
||||
// 失败保留已有会话数据(首次为空则显示错误态),标记失败供 UI 提供重试入口
|
||||
sessionHistoryFailed.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user