Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ui/src/assets/iconfont.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions ui/src/components/COMPONENT_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ Admin、Chat 入口调用 `configureMarkdownEditor()` 配置本地高亮、KaTeX
标题折叠区,`title` 或 `label` 插槽提供标题,默认插槽提供内容。
`defaultExpanded` 默认 `true`,仅设置初始状态;展开状态通过 `v-model:expanded` 同步。
`indicatorPosition` 默认 `before`,可设为 `after`;`triggerClass`、`triggerStyle` 调整触发区。
默认前置使用 14px 三角图标,后置使用 16px `icon_down_outlined`,保留各自展开时的旋转方式。
需要定制时使用 `indicator` 插槽,通过 `{ expanded }` 获取展开状态;图标、尺寸、颜色及旋转由
调用方设置。插槽仍位于组件的切换按钮内,不需要再绑定点击事件。标题悬停联动可通过
`triggerClass="group"` 配合图标的 `group-hover:` 类实现,label 可独立设置颜色。
内容使用 `v-show`,收起时保留内部组件及表单状态。

### MkEmpty
Expand Down
20 changes: 11 additions & 9 deletions ui/src/components/global/mk-collapse/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const props = withDefaults(
{ defaultExpanded: true, indicatorPosition: 'before', trigger: 'header' },
)

defineSlots<{ default?: () => unknown; label?: () => unknown }>()
defineSlots<{ default?: () => unknown; label?: () => unknown; indicator?: (props: { expanded: boolean }) => unknown }>()

const expandedModel = defineModel<boolean>('expanded')
const localExpanded = ref(props.defaultExpanded)
Expand All @@ -42,14 +42,16 @@ const expanded = computed({
:aria-expanded="expanded"
@click.stop="expanded = !expanded"
>
<MkIcon
v-if="indicatorPosition === 'before'"
:icon="CaretBottom"
:size="14"
class="transition-transform text-N600!"
:class="{ '-rotate-90': !expanded }"
/>
<MkIcon v-else name="icon_down_outlined" class="transition-transform text-N600!" :class="{ '-rotate-180': expanded }" />
<slot name="indicator" :expanded="expanded">
<MkIcon
v-if="indicatorPosition === 'before'"
:icon="CaretBottom"
:size="14"
class="transition-transform text-N600!"
:class="{ '-rotate-90': !expanded }"
/>
<MkIcon v-else name="icon_down_outlined" class="transition-transform text-N600!" :class="{ '-rotate-180': expanded }" />
</slot>
</button>
<slot name="label">
<span>{{ title }}</span>
Expand Down
107 changes: 33 additions & 74 deletions ui/src/conversation-panel/components/content/items/reasoning.vue
Original file line number Diff line number Diff line change
@@ -1,83 +1,42 @@
<template>
<div class="reasoning">
<button class="rc-head" @click="isExpanded = !isExpanded">
<svg class="rc-chevron" :class="{ open: isExpanded }" viewBox="0 0 16 16" fill="none">
<path d="M6 4l4 4-4 4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span class="rc-title">思考过程</span>
</button>
<div v-show="isExpanded" class="rc-body">
<pre class="rc-text">{{ content.content }}</pre>
</div>
</div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'

const props = defineProps<{ content: any }>()
defineOptions({ name: 'ConversationReasoningContent' })

const props = defineProps<{ content: { content?: string | null; status?: string } }>()
const isExpanded = ref(true)
let prevStatus = props.content.status

watch(() => props.content.status, (status) => {
if (prevStatus === 'RUNNING' && status !== 'RUNNING') {
isExpanded.value = false
}
prevStatus = status
})
watch(
() => props.content.status,
(status) => {
if (prevStatus === 'RUNNING' && status !== 'RUNNING') {
isExpanded.value = false
}
prevStatus = status
},
)
</script>

<style scoped lang="scss">
.reasoning {
width: 100%;
margin-bottom: 8px;
}

.rc-head {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 3px 0;
border: none;
background: none;
cursor: pointer;
font-family: inherit;
color: var(--t3, #909399);
transition: color 0.15s;
}

.rc-head:hover {
color: var(--t1, #303133);
}

.rc-chevron {
width: 14px;
height: 14px;
flex-shrink: 0;
transition: transform 0.15s;
}

.rc-chevron.open {
transform: rotate(90deg);
}

.rc-title {
font-size: 13px;
font-weight: 500;
color: var(--t2, #606266);
}

.rc-body {
padding: 6px 0 4px 8px;
}
<template>
<!-- 展开或收起思考内容 -->
<MkCollapse v-model:expanded="isExpanded" indicator-position="after" trigger-class="group text-N500! mb-2 w-fit hover:text-N900!">
<template #indicator="{ expanded }">
<MkIcon
name="icon_right_outlined"
:size="12"
class="transition-transform text-N500! group-hover:text-N900!"
:class="{ 'rotate-90': expanded }"
/>
</template>
<template #label>
<MkIcon name="icon_two-ellipses_outlined" />
<span class="-mx-1">思考中</span>
</template>
<div class="text-N500! whitespace-pre-wrap break-words">
{{ content.content }}
</div>
</MkCollapse>
</template>

.rc-text {
margin: 0;
font-size: 13px;
line-height: 1.65;
color: var(--t2, #606266);
white-space: pre-wrap;
word-break: break-word;
font-family: inherit;
}
</style>
<style scoped lang="scss"></style>
8 changes: 7 additions & 1 deletion ui/src/views/VIEW_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,13 @@ View 负责路由、资源与模式、图加载、保存/发布/调试编排
- 模板中心共享 UI 在 `workflow/components/template-store/`,业务入口查模板,View 确认覆盖、提交并重载详情和保存基准。
创建模板由资源创建流程负责,取消/失败保留模板中心。
- 调试与导出先保存未提交修改,保存失败不继续;打开历史、模板或默认模型时按原流程关闭调试。
- 智能体复用对话模块 DebugPanel;工具使用 debug Drawer、SSE 和执行详情;知识库保留任务轮询与导入 Action。
- 智能体页面专属按钮组件放在 `workflow/application/components/`。
`ButtonDebug` 组合调试按钮与对话模块 DebugPanel,通过页面传入的改动检查、保存方法和共享 loading
完成保存后调试流程;公开 `close()` 供页面其他操作关闭调试。
`ButtonAiGenerate` 组合 AI 生成按钮与生成面板;生成面板与调试面板一样自行 Teleport 到 body,
使用固定定位,顶部避开工作流工具栏。
智能体工作流页面通过 `label` 传入 AI 生成、模板中心、默认模型设置、调试和发布历史入口文案。
工具使用 debug Drawer、SSE 和执行详情;知识库保留任务轮询与导入 Action。
智能体画布按 Workspace/System 资源管理选择详情、保存、发布、模型和历史 API;DebugPanel 尚未按 System 范围适配。
客户端停止读取不等于服务端停止任务。
- 知识库导出仅工作流 `.kbwf`;导入确认覆盖后直接上传,不先保存旧草稿,成功重载;取消/失败保留图。
Expand Down
8 changes: 6 additions & 2 deletions ui/src/views/document-detail/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { provideDocumentDetailContext } from './context'
import type { ParamsPage } from '@/api/admin/core/types'
import type { DocumentItem, ParagraphItem, ParagraphQuery } from '@/api/types'
import { getFileIconUrl } from '@/utils/icon'
import { KNOWLEDGE_TYPE_KEY } from '@/constants/knowledge'
import { MsgConfirm, MsgSuccess } from '@/utils/message'
import { useParagraphSort } from './action-dropdown/move/useParagraphSort'
import CreateParagraphDialog from './paragraph-dialog/CreateParagraphDialog.vue'
Expand Down Expand Up @@ -255,8 +256,11 @@ onMounted(() => {
<div class="flex-align-center min-w-0 gap-2">
<!-- 返回文档列表 -->
<el-button text @click="handleBack"><MkIcon name="icon_arrow-left_outlined" :size="20" /></el-button>
<img v-if="documentDetail" :src="getFileIconUrl(documentDetail.name)" alt="" class="size-6 shrink-0" />
<h4 class="truncate" :title="documentDetail?.name">{{ documentDetail?.name ?? '文档分段' }}</h4>
<template v-if="documentDetail">
<img v-if="route.params.type === KNOWLEDGE_TYPE_KEY.WEB" src="@/assets/file-type/web-link-icon.svg" alt="" class="size-6 shrink-0" />
<img v-else :src="getFileIconUrl(documentDetail.name)" alt="" class="size-6 shrink-0" />
</template>
<h4 class="truncate" :title="documentDetail?.name">{{ documentDetail?.name ?? '-' }}</h4>
</div>
<div class="flex-align-center shrink-0 gap-3">
<MkComplexSearch :key="searchKey" :fields="searchFields" @change="handleSearchChange" />
Expand Down
39 changes: 39 additions & 0 deletions ui/src/views/workflow/application/components/ButtonAiGenerate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script setup lang="ts">
import { useTemplateRef } from 'vue'
import type LogicFlow from '@logicflow/core'
import type { DefaultModelSettingPayload } from '@/api/types'
import AiGeneratePanel from '@/workflow-canvas/ai-generate/panel/AiGeneratePanel.vue'
import { WorkflowMode } from '@/workflow-canvas/types'

defineOptions({ name: 'ButtonAiGenerate' })
const props = defineProps<{
label: string
disabled?: boolean
getLogicFlow: () => LogicFlow | undefined
defaultModelSettings: DefaultModelSettingPayload
}>()
const emit = defineEmits<{ open: [] }>()
const aiGeneratePanelRef = useTemplateRef<InstanceType<typeof AiGeneratePanel>>('aiGeneratePanelRef')

/* 打开生成面板前通知页面关闭调试。 */
function handleOpenAiGenerate() {
if (props.disabled) return
emit('open')
aiGeneratePanelRef.value?.open()
}
</script>

<template>
<!-- AI 生成智能体工作流 -->
<el-button plain :disabled="disabled" @click="handleOpenAiGenerate">
<!-- // TODO 图标 -->
<MkIcon name="icon_star" />
<span>{{ label }}</span>
</el-button>
<AiGeneratePanel
ref="aiGeneratePanelRef"
:get-logic-flow="getLogicFlow"
:default-model-settings="defaultModelSettings"
:workflow-mode="WorkflowMode.Application"
/>
</template>
47 changes: 47 additions & 0 deletions ui/src/views/workflow/application/components/ButtonDebug.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script setup lang="ts">
import { useTemplateRef } from 'vue'
import DebugPanel from '@/conversation-panel/view/debug/index.vue'

defineOptions({ name: 'ButtonDebug' })
const props = defineProps<{
label: string
disabled?: boolean
hasUnsavedChanges: () => boolean
saveWorkflow: () => Promise<unknown>
}>()
const loading = defineModel<boolean>('loading', { default: false })
const debugPanelRef = useTemplateRef<InstanceType<typeof DebugPanel>>('debugPanelRef')

/* 调试前保存未提交修改;保存失败时保留画布,不打开调试。 */
async function handleDebug() {
if (loading.value || props.disabled) return
if (props.hasUnsavedChanges()) {
loading.value = true
try {
await props.saveWorkflow()
} catch {
// 保存失败已由页面保存流程提示。
return
} finally {
loading.value = false
}
}
debugPanelRef.value?.open()
}

function close() {
debugPanelRef.value?.close()
}

defineExpose({ close })
</script>

<template>
<!-- 调试智能体工作流 -->
<MkTooltip :content="label" placement="bottom">
<el-button plain :disabled="loading || disabled" @click="handleDebug" class="w-8! min-w-8!">
<MkIcon name="icon_play_outlined" />
</el-button>
</MkTooltip>
<DebugPanel ref="debugPanelRef" />
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import PublishHistoryDrawer from '@/views/workflow/components/publish-history/Pu

defineOptions({ name: 'ButtonApplicationPublishHistory' })

const props = defineProps<{ api: typeof WorkflowApi; applicationId: string; selectedId?: string; disabled?: boolean }>()
const props = defineProps<{ label: string; api: typeof WorkflowApi; applicationId: string; selectedId?: string; disabled?: boolean }>()
const visible = defineModel<boolean>('visible', { default: false })
const emit = defineEmits<{
open: []
Expand Down Expand Up @@ -65,7 +65,7 @@ function handleSubmit(payload: WorkflowVersionPayload, versionId?: string) {
<!-- 打开智能体发布历史 -->
<MkDropdownItem :disabled="disabled || loading || saving" @click="handleOpen">
<template #icon><MkIcon name="icon_history_outlined" /></template>
<span>发布历史</span>
<span>{{ label }}</span>
</MkDropdownItem>
<!-- 发布历史抽屉:由 MkDrawer 按需渲染内容。 -->
<PublishHistoryDrawer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { ApplicationStoreTemplate } from '@/api/types'
import TemplateStoreDialog from '@/views/workflow/components/template-store/TemplateStoreDialog.vue'

defineOptions({ name: 'ButtonApplicationTemplateStore' })
defineProps<{ label: string }>()
const emit = defineEmits<{ open: []; use: [template: ApplicationStoreTemplate] }>()
const templateStoreDialogRef = useTemplateRef<InstanceType<typeof TemplateStoreDialog>>('templateStoreDialogRef')

Expand Down Expand Up @@ -41,7 +42,7 @@ defineExpose({ close })
<!-- 打开模板中心 -->
<el-button plain :disabled="loading" @click="handleOpen">
<MkIcon name="icon_template_outlined" />
<span>模板中心</span>
<span>{{ label }}</span>
</el-button>
<TemplateStoreDialog
ref="templateStoreDialogRef"
Expand Down
Loading
Loading