Files
admin-ui/src/views/home/components/MainContent.vue
T
2910410219andClaude cf6a17387d 修复:子例程引入便签名称兼容模板字段、模板创建成功后刷新首页占位工作流列表
- 工作流管理引入子例程:存名/提示补 flowTemplateName,管理员引入模板时便签不再显示数字 id
- 首页模板补全创建成功:同步刷新占位页工作流列表(MainContent 暴露 loadPlaceWorkflows),点叉取消后可见新建工作流

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 18:25:32 +08:00

285 lines
8.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="main-content">
<Transition name="content-fade" mode="out-in">
<!-- 对话页 有消息或会话结果时展示消息流工作流表单卡片/结果/产出统一在此)。
key 绑定会话 id切换会话时重建内容区触发 Transition 过渡动画固定 key 会复用 DOM 不触发 -->
<div v-if="hasMessages || hasResults" :key="activeHistoryId || 'chat'" class="content-body chat-body">
<ChatList
:messages="messages"
:has-more="hasMore"
:loading-more="loadingMore"
:execute-request="executeRequest"
class="chat-list-scroll"
@delete="emit('delete', $event)"
@form-submit="(msg: any, payload: any) => emit('form-submit', msg, payload)"
@output-preview="(msg: any, output: any) => emit('output-preview', msg, output)"
@output-download="(msg: any, output: any) => emit('output-download', msg, output)"
@output-delete="(msg: any, output: any) => emit('output-delete', msg, output)"
@load-more="emit('load-more', activeHistoryId)"
/>
</div>
<!-- 默认占位 — 无工作流时显示引导 -->
<div v-else :key="'placeholder'" class="content-body placeholder-body">
<div class="placeholder-content">
<h2 class="placeholder-title">你好,我能帮你解决什么问题?</h2>
<p class="placeholder-desc">选择一个工作流,填写参数后一键生成</p>
<div v-if="placeWorkflows.length" class="workflow-cards">
<el-tooltip
v-for="wf in placeWorkflows"
:key="wf.id"
:content="wf.name"
placement="top"
:show-after="150"
>
<button
type="button"
class="workflow-card"
@click="handlePlaceSelect(wf)"
>
<el-icon class="wf-card-icon"><Promotion /></el-icon>
<span class="wf-card-name">{{ wf.name }}</span>
<span v-if="wf.isTemplate" class="wf-card-tag">模板</span>
</button>
</el-tooltip>
<!-- 「更多」常驻入口:点击跳转工作流管理页查看/选择全部,不依赖工作流数量超出上限 -->
<button
type="button"
class="workflow-card wf-card-more"
title="查看全部工作流"
@click="goWorkflowManage"
>
<el-icon class="wf-card-icon"><More /></el-icon>
<span class="wf-card-name">更多工作流</span>
</button>
</div>
<div v-else class="placeholder-empty">暂无工作流,可在工作流管理中创建</div>
</div>
</div>
</Transition>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { getWorkflowList } from '/@/api/settings/creation';
import { Promotion, More } from '@element-plus/icons-vue';
import ChatList from './ChatList.vue';
import type { VOSessionInfoResult } from '/@/api/settings/workflow/session';
interface Props {
activeMenu: string;
activeHistoryId?: string | null;
messages?: any[];
// 会话内结果(session/get):仅用于判断「对话页 vs 占位页」,结果渲染统一走消息流
results?: VOSessionInfoResult[];
hasMore?: boolean;
loadingMore?: boolean;
// InputBar 发送按钮触发执行信号(透传给 ChatList,定位工作流卡片执行)
executeRequest?: { msgId: string; action: 'submit'; nonce: number } | null;
}
interface Emits {
(e: 'workflow-select', id: string, isTemplate?: boolean): void;
(e: 'delete', msg: any): void;
(e: 'form-submit', msg: any, payload: any): void;
(e: 'output-preview', msg: any, output: any): void;
(e: 'output-download', msg: any, output: any): void;
(e: 'output-delete', msg: any, output: any): void;
(e: 'load-more', sid: string | null | undefined): void;
}
const props = withDefaults(defineProps<Props>(), {
activeHistoryId: null,
messages: () => [],
results: () => [],
hasMore: false,
loadingMore: false,
executeRequest: null,
});
const emit = defineEmits<Emits>();
const router = useRouter();
const hasMessages = computed(() => Array.isArray(props.messages) && props.messages.length > 0);
const hasResults = computed(() => Array.isArray(props.results) && props.results.length > 0);
// 占位页工作流卡片(DeepSeek 式引导:居中标题 + 工作流卡片)
// 展示上限与 InputBar 胶囊保持一致:用户工作流优先,不足用模板补足,最多 9 个
const MAX_PLACE_WORKFLOWS = 9;
const placeWorkflows = ref<any[]>([]);
const loadPlaceWorkflows = async () => {
try {
const res: any = await getWorkflowList();
const userList = res.data?.listFlowUserRes?.list || [];
const tplList = res.data?.listFlowTemplateRes?.list || [];
placeWorkflows.value = [
...userList.map((w: any) => ({ id: String(w.id), name: w.flowName || '未命名', isTemplate: false })),
...tplList.map((w: any) => ({ id: String(w.id), name: w.flowTemplateName || '未命名', isTemplate: true })),
].slice(0, MAX_PLACE_WORKFLOWS);
} catch {
placeWorkflows.value = [];
}
};
const handlePlaceSelect = (wf: any) => {
emit('workflow-select', wf.id, wf.isTemplate);
};
// 「更多」入口:工作流数量超出展示上限时跳转工作流管理页查看/选择全部
const goWorkflowManage = () => {
router.push('/settings/workflow');
};
onMounted(() => {
loadPlaceWorkflows();
});
defineExpose({ loadPlaceWorkflows });
</script>
<style scoped lang="scss">
/* ===== 页面切换过渡:淡入淡出 + 轻微上移 ===== */
.content-fade-enter-active,
.content-fade-leave-active {
transition: opacity 0.25s ease, transform 0.25s ease;
}
.content-fade-enter-from {
opacity: 0;
transform: translateY(8px);
}
.content-fade-leave-to {
opacity: 0;
transform: translateY(-8px);
}
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.content-body {
flex: 1;
overflow-y: auto;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar { display: none; }
}
/* ===== 对话页 ===== */
.chat-body {
width: min(880px, calc(100% - 40px));
margin: 0 auto;
height: 100%;
display: flex;
flex-direction: column;
padding: 0;
box-sizing: border-box;
overflow: hidden; /* 结果列表固定高度、消息流内部滚动,避免整体滚动与内部冲突 */
scrollbar-width: none;
&::-webkit-scrollbar { display: none; }
}
/* 消息流填充剩余高度并在内部滚动 */
.chat-body :deep(.chat-list-scroll) {
flex: 1;
min-height: 0;
height: auto;
}
.placeholder-body { display: flex; align-items: center; justify-content: center; }
.placeholder-content { text-align: center; padding: 24px 20px; width: 100%; max-width: 740px; }
/* ===== 标题与描述 ===== */
.placeholder-title { font-size: 26px; font-weight: 700; color: #0f172a; margin: 0 0 8px; letter-spacing: -0.2px; line-height: 1.4; }
.placeholder-desc { font-size: 15px; color: #94a3b8; margin: 0 0 24px; line-height: 1.5; }
/* ===== 工作流功能卡片(DeepSeek 式引导) ===== */
.workflow-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 12px;
text-align: left;
}
.workflow-card {
display: flex;
align-items: center;
gap: 10px;
padding: 18px 20px;
border: 1px solid #e5e8ee;
border-radius: 14px;
background: #fff;
cursor: pointer;
transition: all 0.15s;
outline: none;
&:hover {
border-color: #93c5fd;
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.12);
transform: translateY(-1px);
}
}
.wf-card-icon {
width: 40px;
height: 40px;
border-radius: 12px;
background: #eaf1ff;
color: #2563eb;
font-size: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.wf-card-name {
flex: 1;
min-width: 0;
font-size: 15px;
font-weight: 600;
color: #1e293b;
line-height: 1.4;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.wf-card-tag {
font-size: 10px;
padding: 2px 6px;
border-radius: 4px;
background: #fff7e6;
color: #d97706;
border: 1px solid #fde68a;
white-space: nowrap;
}
/* 「更多」入口:与普通工作流卡片区分,虚线边框 + 更轻的视觉重量,提示可进入管理页查看全部 */
.wf-card-more {
border-style: dashed;
justify-content: center;
.wf-card-icon {
background: #f8fafc;
color: #94a3b8;
}
.wf-card-name {
color: #94a3b8;
font-weight: 500;
}
&:hover {
border-style: dashed;
border-color: #93c5fd;
.wf-card-icon {
background: #eaf1ff;
color: #2563eb;
}
.wf-card-name {
color: #2563eb;
}
}
}
.placeholder-empty {
font-size: 13px;
color: #cbd5e1;
padding: 16px 0;
}
</style>