新增历史任务详情页
This commit is contained in:
parent
94cf2be101
commit
d08db8a76a
@ -13,9 +13,12 @@ export default {
|
|||||||
copy: "Copy",
|
copy: "Copy",
|
||||||
paste: "Paste",
|
paste: "Paste",
|
||||||
cut: "Cut",
|
cut: "Cut",
|
||||||
|
baseInfo: "Base Info",
|
||||||
|
|
||||||
createdDt: "Created Date",
|
createdDt: "Created Date",
|
||||||
updatedDt: "Updated Date",
|
updatedDt: "Updated Date",
|
||||||
|
noData: "No Data",
|
||||||
|
|
||||||
menu: {
|
menu: {
|
||||||
task: "Task",
|
task: "Task",
|
||||||
history: "History",
|
history: "History",
|
||||||
|
@ -13,9 +13,12 @@ export default {
|
|||||||
copy: "复制",
|
copy: "复制",
|
||||||
paste: "粘贴",
|
paste: "粘贴",
|
||||||
cut: "剪切",
|
cut: "剪切",
|
||||||
|
baseInfo: "基本信息",
|
||||||
|
|
||||||
createdDt: "创建时间",
|
createdDt: "创建时间",
|
||||||
updatedDt: "更新时间",
|
updatedDt: "更新时间",
|
||||||
|
noData: "暂无数据",
|
||||||
|
|
||||||
menu: {
|
menu: {
|
||||||
task: "任务",
|
task: "任务",
|
||||||
history: "历史记录",
|
history: "历史记录",
|
||||||
|
@ -32,7 +32,7 @@ const router = createRouter({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'history',
|
path: 'history',
|
||||||
component: () => import('@/views/task/History.vue'),
|
component: () => import('@/views/task/HistoryIndex.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
title: "历史记录",
|
title: "历史记录",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<template :lang="i18n.locale">
|
<template>
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<el-card>
|
<el-card>
|
||||||
<template #header>
|
<template #header>
|
||||||
@ -54,11 +54,11 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, inject, computed, onMounted, onBeforeUnmount, onUnmounted, watch } from 'vue'
|
import { ref, reactive, inject, computed, onMounted, onBeforeUnmount, onUnmounted, watch } from 'vue'
|
||||||
import { FolderAdd, Promotion, Eleme, CircleClose } from '@element-plus/icons-vue'
|
import { useRouter } from 'vue-router'
|
||||||
import { useConfig } from '@/store/config'
|
import { useConfig } from '@/store/config'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import i18n from '@/locales/i18n'
|
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
const utils = inject('utils')
|
const utils = inject('utils')
|
||||||
const config = useConfig()
|
const config = useConfig()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@ -171,7 +171,6 @@ function search() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleSelectionChange = (val) => {
|
const handleSelectionChange = (val) => {
|
||||||
selectedRows.value = val
|
selectedRows.value = val
|
||||||
}
|
}
|
||||||
@ -229,120 +228,10 @@ function resetSearch() {
|
|||||||
|
|
||||||
function toTaskInfo(taskId) {
|
function toTaskInfo(taskId) {
|
||||||
console.log("toTaskInfo:", taskId)
|
console.log("toTaskInfo:", taskId)
|
||||||
|
router.push("/task/"+taskId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
.output-area {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-user {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: space-between;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-user .blank {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-user .content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: end;
|
|
||||||
border-radius: 12px;
|
|
||||||
background-color: var(--el-fg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-user .title {
|
|
||||||
/** 防止子元素宽度被设置为100%, 子元素的align-self设置除auto和stretch之外的值 */
|
|
||||||
align-self: flex-end;
|
|
||||||
margin: 6px 16px;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-user .prompt {
|
|
||||||
/** 防止子元素宽度被设置为100%, 子元素的align-self设置除auto和stretch之外的值 */
|
|
||||||
align-self: flex-end;
|
|
||||||
margin: 0px 16px 6px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.dialog {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.dialog-ai {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
background-color: var(--el-fg-color);
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-ai .title {
|
|
||||||
margin: 6px 12px;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-area {
|
|
||||||
flex-grow: 0;
|
|
||||||
width: 100%;
|
|
||||||
max-height: 180px;
|
|
||||||
padding-left: 80px;
|
|
||||||
padding-right: 80px;
|
|
||||||
padding-top: 12px;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-box {
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 16px;
|
|
||||||
background-color: var(--el-fg-color);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-style {
|
|
||||||
width: 100%;
|
|
||||||
padding-top: 12px;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-style :deep(.el-textarea__inner) {
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
resize: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-file-area {
|
|
||||||
margin-left: 16px;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.send-area {
|
|
||||||
margin-left: 8px;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tips {
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
font-size: 12px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub-step-time {
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,4 +1,4 @@
|
|||||||
<template :lang="i18n.locale">
|
<template>
|
||||||
<div class="main-content fc">
|
<div class="main-content fc">
|
||||||
<el-scrollbar ref="scrollRef" style="width: 100%;">
|
<el-scrollbar ref="scrollRef" style="width: 100%;">
|
||||||
<div class="output-area" v-show="taskInfo.taskId != null">
|
<div class="output-area" v-show="taskInfo.taskId != null">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<template :lang="i18n.locale">
|
<template>
|
||||||
<div class="main-content fc">
|
<div class="main-content fc">
|
||||||
<el-scrollbar ref="scrollRef">
|
<el-scrollbar ref="scrollRef">
|
||||||
<div class="output-area" v-show="taskInfo.taskId != null">
|
<div class="output-area" v-show="taskInfo.taskId != null">
|
||||||
@ -6,9 +6,12 @@
|
|||||||
<div class="dialog-user">
|
<div class="dialog-user">
|
||||||
<div class="blank"></div>
|
<div class="blank"></div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<el-text class="title">
|
<div class="title fxc">
|
||||||
{{ t('user') }}
|
<img src="@/assets/img/user.png" class="user-img"/>
|
||||||
</el-text>
|
<el-text>
|
||||||
|
{{ t('user') }}
|
||||||
|
</el-text>
|
||||||
|
</div>
|
||||||
<el-text class="prompt">
|
<el-text class="prompt">
|
||||||
{{ taskInfo.prompt }}
|
{{ taskInfo.prompt }}
|
||||||
</el-text>
|
</el-text>
|
||||||
@ -84,7 +87,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, inject, computed, onMounted, onUnmounted } from 'vue'
|
import { ref, reactive, inject, computed, onMounted, onUnmounted } from 'vue'
|
||||||
import { FolderAdd, Promotion, Eleme, CircleClose } from '@element-plus/icons-vue'
|
import { FolderAdd, Promotion, User, CircleClose } from '@element-plus/icons-vue'
|
||||||
import { useConfig } from '@/store/config'
|
import { useConfig } from '@/store/config'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
@ -296,6 +299,7 @@ function stop() {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.output-area {
|
.output-area {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-user {
|
.dialog-user {
|
||||||
@ -331,12 +335,17 @@ function stop() {
|
|||||||
margin: 0px 16px 6px 16px;
|
margin: 0px 16px 6px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialog-user .user-img{
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 2px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.dialog {
|
.dialog {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.dialog-ai {
|
.dialog-ai {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
background-color: var(--el-fg-color);
|
background-color: var(--el-fg-color);
|
||||||
|
@ -1,302 +1,105 @@
|
|||||||
<template :lang="i18n.locale">
|
<template>
|
||||||
<div class="main-content fc">
|
<div class="main-content fc">
|
||||||
<el-scrollbar ref="scrollRef">
|
<!-- 展示模块-暂无数据 -->
|
||||||
<div class="output-area" v-show="taskInfo.taskId != null">
|
<div class="no-data" v-show="baseNoData">{{ t('noData') }}</div>
|
||||||
|
|
||||||
<div class="dialog-user">
|
<!-- 展示模块 -->
|
||||||
<div class="blank"></div>
|
<div class="output-area" v-show="baseShow">
|
||||||
<div class="content">
|
|
||||||
<el-text class="title">
|
<div class="dialog-user">
|
||||||
|
<div class="blank"></div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="title fxc">
|
||||||
|
<img src="@/assets/img/user.png" class="user-img" />
|
||||||
|
<el-text>
|
||||||
{{ t('user') }}
|
{{ t('user') }}
|
||||||
</el-text>
|
</el-text>
|
||||||
<el-text class="prompt">
|
|
||||||
{{ taskInfo.prompt }}
|
|
||||||
</el-text>
|
|
||||||
</div>
|
</div>
|
||||||
|
<el-text class="prompt">
|
||||||
|
{{ taskInfo.prompt }}
|
||||||
|
</el-text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-ai">
|
|
||||||
<el-text class="title"> OpenManus-AI </el-text>
|
|
||||||
|
|
||||||
<div class="card-row-wrap">
|
|
||||||
<div class="card-row-aline">
|
|
||||||
<el-timeline class="wp-100">
|
|
||||||
<el-timeline-item v-for="(step, index) in taskInfo.stepList" :key="index" :timestamp="step.createdDt"
|
|
||||||
placement="top">
|
|
||||||
<el-card>
|
|
||||||
<div>
|
|
||||||
<h4 class="color-label mr-10" :class="utils.colorByLabel('step')">
|
|
||||||
{{ t('step') }}
|
|
||||||
</h4>
|
|
||||||
<el-text>{{ step.result }}</el-text>
|
|
||||||
</div>
|
|
||||||
<el-divider />
|
|
||||||
<div v-for="(subStep, subIndex) in step.subList">
|
|
||||||
<div class="fxsb mtb-10">
|
|
||||||
<el-text> {{ subStep.type }} </el-text>
|
|
||||||
<el-text class="sub-step-time"> {{ subStep.createdDt }} </el-text>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-text> {{ subStep.result }} </el-text>
|
|
||||||
</div>
|
|
||||||
<el-divider v-if="subIndex != step.subList.length - 1" />
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-timeline-item>
|
|
||||||
</el-timeline>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-text class="pr-10">{{ t('taskStatus.name') }}:</el-text>
|
|
||||||
<el-text>{{ taskInfo.status }}</el-text>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
|
||||||
|
|
||||||
<div class="input-area">
|
<div class="dialog-ai">
|
||||||
<div class="input-box">
|
<el-text class="title"> OpenManus-AI </el-text>
|
||||||
<el-icon @click="uploadFile" class="add-file-area" :size="24">
|
|
||||||
<FolderAdd />
|
|
||||||
</el-icon>
|
|
||||||
<el-input ref="promptEle" type="textarea" v-model="prompt" class="input-style" style="border: none;"
|
|
||||||
:autosize="{ minRows: 1, maxRows: 4 }" autofocus :placeholder="t('promptInputPlaceHolder')"
|
|
||||||
@keydown.enter="handleInputEnter" />
|
|
||||||
|
|
||||||
<el-link class="send-area">
|
<div class="card-row-wrap">
|
||||||
<el-icon @click="sendPrompt" :size="24" v-show="!loading">
|
<div class="card-row-aline">
|
||||||
<Promotion />
|
<el-timeline class="wp-100">
|
||||||
</el-icon>
|
<el-timeline-item v-for="(step, index) in taskInfo.stepList" :key="index" :timestamp="step.createdDt"
|
||||||
<el-icon @click="stop" :size="24" v-show="loading">
|
placement="top">
|
||||||
<CircleClose />
|
<el-card>
|
||||||
</el-icon>
|
<div>
|
||||||
</el-link>
|
<h4 class="color-label mr-10" :class="utils.colorByLabel('step')">
|
||||||
|
{{ t('step') }}
|
||||||
|
</h4>
|
||||||
|
<el-text>{{ step.result }}</el-text>
|
||||||
|
</div>
|
||||||
|
<el-divider />
|
||||||
|
<div v-for="(subStep, subIndex) in step.subList">
|
||||||
|
<div class="fxsb mtb-10">
|
||||||
|
<el-text> {{ subStep.type }} </el-text>
|
||||||
|
<el-text class="sub-step-time"> {{ subStep.createdDt }} </el-text>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<el-text> {{ subStep.result }} </el-text>
|
||||||
|
</div>
|
||||||
|
<el-divider v-if="subIndex != step.subList.length - 1" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<el-text class="tips">{{ t('openManusAgiTips') }}</el-text>
|
<el-text class="pr-10">{{ t('taskStatus.name') }}:</el-text>
|
||||||
|
<el-text>{{ taskInfo.status }}</el-text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, inject, computed, onMounted, onUnmounted } from 'vue'
|
import { ref, reactive, inject, computed, onMounted, onUnmounted } from 'vue'
|
||||||
import { FolderAdd, Promotion, Eleme, CircleClose } from '@element-plus/icons-vue'
|
import { User } from '@element-plus/icons-vue'
|
||||||
import { useConfig } from '@/store/config'
|
import { useConfig } from '@/store/config'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import i18n from '@/locales/i18n'
|
|
||||||
|
|
||||||
const utils = inject('utils')
|
const utils = inject('utils')
|
||||||
const config = useConfig()
|
const config = useConfig()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const prompt = ref('')
|
// 视图模式
|
||||||
const promptEle = ref(null)
|
const viewModel = reactive({
|
||||||
|
base: 'show'
|
||||||
|
})
|
||||||
|
|
||||||
const eventTypes = ['think', 'tool', 'act', 'log', 'run', 'message']
|
const baseShow = computed(() => {
|
||||||
const eventSource = ref(null)
|
return viewModel.base == 'show' || viewModel.base == 'showMore'
|
||||||
|
})
|
||||||
|
|
||||||
|
const baseNoData = computed(() => {
|
||||||
|
return baseShow && taskInfo.value == null
|
||||||
|
})
|
||||||
|
|
||||||
const taskInfo = computed(() => {
|
const taskInfo = computed(() => {
|
||||||
return config.getCurrTask()
|
return config.getCurrTask()
|
||||||
})
|
})
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
const scrollRef = ref(null)
|
|
||||||
|
|
||||||
// 建立EventSource连接
|
|
||||||
const buildEventSource = (taskId) => {
|
|
||||||
loading.value = true
|
|
||||||
eventSource.value = new EventSource('http://localhost:5172/tasks/' + taskId + '/events')
|
|
||||||
eventSource.value.onmessage = (event) => {
|
|
||||||
console.log('Received data:', event.data)
|
|
||||||
// 在这里处理接收到的数据 不起作用
|
|
||||||
}
|
|
||||||
|
|
||||||
eventTypes.forEach(type => {
|
|
||||||
eventSource.value.addEventListener(type, (event) => handleEvent(event, type))
|
|
||||||
})
|
|
||||||
|
|
||||||
eventSource.value.onerror = (error) => {
|
|
||||||
console.error('EventSource failed:', error)
|
|
||||||
// 处理错误情况
|
|
||||||
loading.value = false
|
|
||||||
eventSource.value.close()
|
|
||||||
taskInfo.value.status = "taskStatus.failed"
|
|
||||||
utils.pop("任务执行失败", "error")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleEvent = (event, type) => {
|
|
||||||
console.log('Received event, type:', type, event.data)
|
|
||||||
// clearInterval(heartbeatTimer);
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
console.log("type:", type, "data:", data)
|
|
||||||
if (eventSource.value.readyState === EventSource.CLOSED) {
|
|
||||||
console.log('Connection is closed');
|
|
||||||
}
|
|
||||||
if (type == "complete" || data.status == "completed") {
|
|
||||||
console.log('task completed');
|
|
||||||
loading.value = false
|
|
||||||
eventSource.value.close()
|
|
||||||
taskInfo.value.status = "taskStatus.success"
|
|
||||||
utils.pop("任务已完成", "success")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// autoScroll(stepContainer);
|
|
||||||
buildOutput(taskInfo.value.taskId)
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Error handling ${type} event:`, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function buildOutput(taskId) {
|
|
||||||
// 同步执行,确保数据顺序
|
|
||||||
await utils.awaitGet('http://localhost:5172/tasks/' + taskId).then(data => {
|
|
||||||
console.log("task info resp:", data)
|
|
||||||
buildStepList(data.steps)
|
|
||||||
console.log("stepList:", taskInfo.value.stepList)
|
|
||||||
// 滚动到底部
|
|
||||||
setTimeout(() => {
|
|
||||||
scrollToBottom()
|
|
||||||
}, 100)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 封装stepList
|
|
||||||
const buildStepList = (steps) => {
|
|
||||||
// stepList
|
|
||||||
steps.forEach((step, idx) => {
|
|
||||||
// 步骤
|
|
||||||
if (step.type == "log" && step.result.startsWith("Executing step")) {
|
|
||||||
const stepStr = step.result.replace("Executing step ", "").replace("\n", "")
|
|
||||||
const stepNo = stepStr.split("/")[0]
|
|
||||||
if (taskInfo.value.stepList.length < stepNo) {
|
|
||||||
// 添加此step到stepList
|
|
||||||
const parentStep = {
|
|
||||||
type: "log",
|
|
||||||
idx: idx,
|
|
||||||
stepNo: stepNo,
|
|
||||||
result: stepStr,
|
|
||||||
subList: [],
|
|
||||||
createdDt: utils.dateFormat(new Date())
|
|
||||||
}
|
|
||||||
taskInfo.value.stepList.push(parentStep)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 子步骤
|
|
||||||
const subStep = {
|
|
||||||
type: step.type,
|
|
||||||
idx: idx,
|
|
||||||
result: step.result,
|
|
||||||
createdDt: utils.dateFormat(new Date())
|
|
||||||
}
|
|
||||||
// 判定添加到stepList中的哪个元素元素的subList中
|
|
||||||
console.log("stepList:", taskInfo.value.stepList, "idx:", idx)
|
|
||||||
let parentStep = null
|
|
||||||
const pStepIndex = taskInfo.value.stepList.findIndex(parentStep => parentStep.idx > idx)
|
|
||||||
console.log("pStepIndex:", pStepIndex)
|
|
||||||
if (pStepIndex != -1) {
|
|
||||||
// 取pStep的上一个元素
|
|
||||||
parentStep = taskInfo.value.stepList[pStepIndex - 1]
|
|
||||||
} else {
|
|
||||||
// 不存在时, 添加到stepList最后一个元素末尾
|
|
||||||
parentStep = taskInfo.value.stepList[taskInfo.value.stepList.length - 1]
|
|
||||||
}
|
|
||||||
console.log("parentStep:", parentStep)
|
|
||||||
const existSubStep = parentStep.subList.find(existSubStep => existSubStep.idx == idx)
|
|
||||||
if (!existSubStep) {
|
|
||||||
// 不存在时, 添加到末尾
|
|
||||||
parentStep.subList.push(subStep)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
// 组件卸载时关闭EventSource连接
|
|
||||||
if (eventSource.value) {
|
|
||||||
eventSource.value.close()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function handleInputEnter(event) {
|
|
||||||
console.log("handleInputEnter:", event)
|
|
||||||
event.preventDefault()
|
|
||||||
sendPrompt()
|
|
||||||
}
|
|
||||||
|
|
||||||
function uploadFile() {
|
|
||||||
utils.pop("暂不支持,开发中", "warning")
|
|
||||||
}
|
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
|
||||||
if (scrollRef.value) {
|
|
||||||
console.log("scrollRef:", scrollRef.value, scrollRef.value.wrapRef)
|
|
||||||
const container = scrollRef.value.wrapRef
|
|
||||||
if (container) {
|
|
||||||
container.scrollTop = container.scrollHeight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送提示词
|
|
||||||
function sendPrompt() {
|
|
||||||
// 关闭之前的连接
|
|
||||||
if (eventSource.value != null) {
|
|
||||||
eventSource.value.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (utils.isBlank(prompt.value)) {
|
|
||||||
utils.pop("Please enter a valid prompt", "error")
|
|
||||||
promptEle.value.focus()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.post('http://localhost:5172/tasks', { prompt: prompt.value }).then(data => {
|
|
||||||
if (!data.task_id) {
|
|
||||||
throw new Error('Invalid task ID')
|
|
||||||
}
|
|
||||||
const newTask = {
|
|
||||||
taskId: data.task_id,
|
|
||||||
prompt: prompt.value,
|
|
||||||
status: "running",
|
|
||||||
createdDt: utils.dateFormat(new Date()),
|
|
||||||
stepList: []
|
|
||||||
}
|
|
||||||
// 保存历史记录
|
|
||||||
config.addTaskHistory(newTask)
|
|
||||||
// 发送完成后清空输入框
|
|
||||||
prompt.value = ''
|
|
||||||
// 建立新的EventSource连接
|
|
||||||
buildEventSource(data.task_id)
|
|
||||||
|
|
||||||
console.log("new task created:", newTask)
|
|
||||||
}).catch(error => {
|
|
||||||
console.error('Failed to create task:', error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function stop() {
|
|
||||||
console.log("stop")
|
|
||||||
loading.value = false
|
|
||||||
eventSource.value.close()
|
|
||||||
taskInfo.value.status = "taskStatus.terminated"
|
|
||||||
utils.pop("用户终止任务", "error")
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.output-area {
|
.output-area {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-user {
|
.dialog-user {
|
||||||
@ -332,6 +135,12 @@ function stop() {
|
|||||||
margin: 0px 16px 6px 16px;
|
margin: 0px 16px 6px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialog-user .user-img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 2px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.dialog {
|
.dialog {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user