更新数字人创作页面功能
- 在树节点中添加点击事件处理,支持工作流节点的详细信息获取和会话ID管理。 - 修改节点操作逻辑,优化预览和下载功能,支持标题节点的操作。 - 引入会话ID管理,确保在工作流切换时正确处理会话状态。 - 更新相关状态和逻辑,提升用户交互体验和数据一致性。
This commit is contained in:
@@ -15,11 +15,12 @@
|
||||
default-expand-all
|
||||
:highlight-current="true"
|
||||
:expand-on-click-node="false"
|
||||
@node-click="handleTreeNodeClick"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<div class="tree-node">
|
||||
<span class="ellipsis">{{ data.label }}</span>
|
||||
<div v-if="data.nodeType === 'html' || data.nodeType === 'image'" class="tree-node-actions">
|
||||
<div v-if="data.nodeType === 'title' && data.fileUrl" class="tree-node-actions">
|
||||
<el-button type="primary" link size="small" @click.stop="previewNode(data)"> 预览 </el-button>
|
||||
<el-button type="primary" link size="small" @click.stop="downloadNode(data)"> 下载 </el-button>
|
||||
</div>
|
||||
@@ -497,7 +498,9 @@
|
||||
<div class="chat-model-selector">
|
||||
<div class="chat-model-search">
|
||||
<el-input v-model="chatModelSearchKeyword" placeholder="搜索模型名称" clearable @clear="handleChatModelSearch">
|
||||
<template #prefix><el-icon><Search /></el-icon></template>
|
||||
<template #prefix
|
||||
><el-icon><Search /></el-icon
|
||||
></template>
|
||||
</el-input>
|
||||
<el-button type="primary" @click="handleChatModelSearch">搜索</el-button>
|
||||
</div>
|
||||
@@ -584,6 +587,8 @@ interface TreeNode {
|
||||
nodeType: NodeType;
|
||||
children?: TreeNode[];
|
||||
fileUrl?: string;
|
||||
flowId?: number | string;
|
||||
sessionId?: string;
|
||||
}
|
||||
interface SelectedState {
|
||||
id: string;
|
||||
@@ -632,6 +637,7 @@ const userInput = ref('');
|
||||
const selectedFiles = ref<File[]>([]);
|
||||
const selectedCreationSkill = ref<SkillItem | null>(null);
|
||||
const showCreationSkillSelector = ref(false);
|
||||
const currentSessionId = ref<string | null>(null); // 当前会话的 sessionId(从工作空间进入时使用)
|
||||
const isCreating = ref(false);
|
||||
// 预览相关状态
|
||||
const previewDialogVisible = ref(false);
|
||||
@@ -654,13 +660,14 @@ const chatModelPagination = reactive({
|
||||
const filteredChatModels = computed(() => {
|
||||
return chatModelList.value;
|
||||
});
|
||||
// 会话ID管理(存储在 sessionStorage 中)
|
||||
// 会话ID管理(每次使用工作流时生成新的 sessionId)
|
||||
const getSessionId = () => {
|
||||
let sessionId = sessionStorage.getItem('ai_creation_session_id');
|
||||
if (!sessionId) {
|
||||
sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
sessionStorage.setItem('ai_creation_session_id', sessionId);
|
||||
// 如果从工作空间进入,使用当前会话的 sessionId
|
||||
if (currentSessionId.value) {
|
||||
return currentSessionId.value;
|
||||
}
|
||||
// 否则生成新的 sessionId
|
||||
const sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
return sessionId;
|
||||
};
|
||||
// 格式化参数引用显示
|
||||
@@ -850,18 +857,13 @@ const buildTreeNodes = (tree: ExecutionTreeItem[]): TreeNode[] =>
|
||||
id: `flow-${di}-${fi}`,
|
||||
label: f.flowName || '未命名工作流',
|
||||
nodeType: 'contentType',
|
||||
flowId: f.flowId,
|
||||
sessionId: f.sessionId, // 添加 sessionId
|
||||
children: (f.items || []).map((item, ii) => ({
|
||||
id: `item-${di}-${fi}-${ii}`,
|
||||
label: item.label || `作品${ii + 1}`,
|
||||
nodeType: 'title',
|
||||
children: [
|
||||
{
|
||||
id: `html-${di}-${fi}-${ii}`,
|
||||
label: 'HTML',
|
||||
nodeType: 'html' as const,
|
||||
fileUrl: item.content,
|
||||
},
|
||||
],
|
||||
fileUrl: item.content, // 直接在作品层添加 fileUrl
|
||||
})),
|
||||
})),
|
||||
}));
|
||||
@@ -934,6 +936,7 @@ const createNewWorkflow = () => {
|
||||
// 切换回画布编辑模式
|
||||
isCreationMode.value = false;
|
||||
currentWorkflowForCreation.value = null;
|
||||
currentSessionId.value = null; // 清空会话 ID
|
||||
|
||||
// 清空当前编辑状态
|
||||
currentEditingWorkflowId.value = null;
|
||||
@@ -1006,7 +1009,7 @@ const handleChatModelSearch = () => {
|
||||
// 设置对话模型
|
||||
const handleSetChatModel = async () => {
|
||||
if (!selectedChatModel.value) return;
|
||||
|
||||
|
||||
settingChatModel.value = true;
|
||||
try {
|
||||
await updateChatModel({
|
||||
@@ -1085,6 +1088,7 @@ const editWorkflow = async (workflow: WorkflowItem) => {
|
||||
// 切换回画布编辑模式
|
||||
isCreationMode.value = false;
|
||||
currentWorkflowForCreation.value = null;
|
||||
currentSessionId.value = null; // 清空会话 ID
|
||||
|
||||
// 等待 DOM 更新后再加载工作流
|
||||
await nextTick();
|
||||
@@ -1105,6 +1109,7 @@ const editWorkflow = async (workflow: WorkflowItem) => {
|
||||
const backToCanvas = async () => {
|
||||
isCreationMode.value = false;
|
||||
currentWorkflowForCreation.value = null;
|
||||
currentSessionId.value = null; // 清空会话 ID
|
||||
|
||||
// 等待 DOM 更新后重新渲染画布
|
||||
await nextTick();
|
||||
@@ -1172,9 +1177,11 @@ const sendMessage = async () => {
|
||||
ElMessageBox.alert('请先设置对话模型后再进行创作', '提示', {
|
||||
confirmButtonText: '去设置',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
showChatModelSelector.value = true;
|
||||
}).catch(() => {});
|
||||
})
|
||||
.then(() => {
|
||||
showChatModelSelector.value = true;
|
||||
})
|
||||
.catch(() => {});
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1281,9 +1288,66 @@ const getFieldClass = (type: string) => {
|
||||
if (type === 'number' || type === 'switch') return 'form-item-small';
|
||||
return 'form-item-medium';
|
||||
};
|
||||
// 处理树节点点击
|
||||
const handleTreeNodeClick = async (data: TreeNode) => {
|
||||
// 只处理工作流节点(contentType)
|
||||
if (data.nodeType === 'contentType' && data.flowId) {
|
||||
try {
|
||||
// 调用详情接口获取工作流数据,使用 flowId
|
||||
const res = await getWorkflowDetail(data.flowId);
|
||||
if (res.data) {
|
||||
// 设置当前会话的 sessionId(从工作空间进入)
|
||||
currentSessionId.value = data.sessionId || null;
|
||||
|
||||
// 切换到创作模式
|
||||
isCreationMode.value = true;
|
||||
currentWorkflowForCreation.value = res.data;
|
||||
|
||||
// 初始化创作表单的值
|
||||
Object.keys(creationFormValues).forEach((key) => delete creationFormValues[key]);
|
||||
|
||||
// 根据 nodeInputParams 初始化表单默认值
|
||||
if (res.data.nodeInputParams && Array.isArray(res.data.nodeInputParams)) {
|
||||
res.data.nodeInputParams.forEach((node: any) => {
|
||||
// 从节点根级别的 formConfig 读取
|
||||
if (node.formConfig && Array.isArray(node.formConfig)) {
|
||||
node.formConfig.forEach((field: any) => {
|
||||
const fieldKey = `${node.id}_${field.label}`;
|
||||
// 根据字段类型转换值
|
||||
if (field.type === 'number') {
|
||||
creationFormValues[fieldKey] = field.value ? Number(field.value) : null;
|
||||
} else if (field.type === 'switch') {
|
||||
creationFormValues[fieldKey] = Boolean(field.value);
|
||||
} else {
|
||||
creationFormValues[fieldKey] = field.value || '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化其他配置字段(从 config 中读取)
|
||||
if (node.config) {
|
||||
Object.keys(node.config).forEach((key) => {
|
||||
if (!['nodeCode', 'width', 'height', 'x', 'y', 'formConfig', 'inputSource', 'fieldMetadata', 'selectedModel'].includes(key)) {
|
||||
const fieldKey = `${node.id}_${key}`;
|
||||
creationFormValues[fieldKey] = node.config[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ElMessage.success(`已进入创作模式`);
|
||||
} else {
|
||||
ElMessage.warning('该工作流没有内容');
|
||||
}
|
||||
} catch (error) {
|
||||
// 后端错误会自动显示
|
||||
}
|
||||
}
|
||||
};
|
||||
// 预览节点
|
||||
const previewNode = (d: TreeNode) => {
|
||||
if (d.nodeType !== 'html' && d.nodeType !== 'image') return;
|
||||
if (d.nodeType !== 'html' && d.nodeType !== 'image' && d.nodeType !== 'title') return;
|
||||
const url = buildAssetUrl(d.fileUrl);
|
||||
if (!url) return ElMessage.warning('当前节点没有可用预览地址');
|
||||
previewUrl.value = url;
|
||||
@@ -1291,14 +1355,16 @@ const previewNode = (d: TreeNode) => {
|
||||
};
|
||||
// 下载节点
|
||||
const downloadNode = async (d: TreeNode) => {
|
||||
if (d.nodeType !== 'html' && d.nodeType !== 'image') return;
|
||||
if (d.nodeType !== 'html' && d.nodeType !== 'image' && d.nodeType !== 'title') return;
|
||||
if (!d.fileUrl) return ElMessage.warning('当前节点没有可下载地址');
|
||||
try {
|
||||
// 下载失败时希望展示更贴近页面语义的提示,因此改为 page 模式。
|
||||
const r = await downloadToFile({ fileURL: d.fileUrl });
|
||||
const blob = r instanceof Blob ? r : r?.data;
|
||||
if (!(blob instanceof Blob)) throw new Error('invalid blob');
|
||||
const name = decodeURIComponent(d.fileUrl.split('/').pop() || `${d.label}.${d.nodeType === 'html' ? 'html' : 'png'}`);
|
||||
const fileName = d.fileUrl.split('/').pop() || '';
|
||||
const fileExt = fileName.split('.').pop()?.toLowerCase() || 'html';
|
||||
const name = decodeURIComponent(fileName || `${d.label}.${fileExt}`);
|
||||
const u = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = u;
|
||||
@@ -2268,7 +2334,7 @@ onBeforeUnmount(() => {
|
||||
.creation-page {
|
||||
height: calc(100vh - 100px);
|
||||
display: grid;
|
||||
grid-template-columns: 320px minmax(0, 1fr) 280px;
|
||||
grid-template-columns: 320px minmax(0, 1fr) 340px;
|
||||
gap: 14px;
|
||||
padding: 14px;
|
||||
background: #f6f8fb;
|
||||
|
||||
Reference in New Issue
Block a user