Version: 0.9.32.dev.260419
后端: 1. 会话历史接口切换为统一时间线读取,并兼容 extra.resume 恢复协议 - api/agent.go:新增 resume->confirm_action 映射(approve/reject/cancel),恢复请求缺 conversation_id 时拦截;GetConversationHistory 改为 GetConversationTimeline - routers/routers.go:路由从 GET /conversation-history 切换为 GET /conversation-timeline - model/agent.go:删除 GetConversationHistoryItem 旧 DTO 2. 新增会话时间线持久化链路(MySQL + Redis) - 新增 model/agent_timeline.go:定义 timeline kind、AgentTimelineEvent、持久化/返回结构 - 新增 dao/agent_timeline.go:写入事件、按 seq 查询、查询 max seq - inits/mysql.go:AutoMigrate 增加 AgentTimelineEvent - dao/cache.go:新增 timeline list/seq key,支持 incr/set seq、append/list、全量回填与删除 - 新增 service/agentsvc/agent_timeline.go:时间线读写编排(Redis 优先、DB 回源、seq 分配与冲突重试、extra 事件映射) 3. 聊天主链路改为写入 timeline,旧 history 服务下线 - service/agentsvc/agent.go:普通聊天用户/助手消息改为 appendConversationTimelineEvent - service/agentsvc/agent_newagent.go:透传 resume_interaction_id;注入 emitter extra hook 持久化卡片事件;正文写入 timeline - 删除 service/agentsvc/agent_history.go:下线 conversation-history 旧缓存编排 4. newAgent 恢复与确认防串单增强 - newAgent/model/graph_run_state.go:AgentGraphRequest 新增 ResumeInteractionID - newAgent/node/agent_nodes.go:透传 ResumeInteractionID - newAgent/node/chat.go:增加 stale_resume 校验;accept/reject 兼容 approve/cancel;非法动作返回 invalid_confirm_action - newAgent/stream/emitter.go:新增 extraEventHook / SetExtraEventHook,在 extra-only 与 confirm 事件触发 5. 日程暂存后同步刷新预览缓存,避免读到拖拽前旧数据 - service/agentsvc/agent_schedule_state.go:Save 后重建并覆盖 preview 缓存,保留 trace/candidate 等字段 6. 缓存失效策略调整到 timeline 口径 - middleware/cache_deleter.go:移除 conversation-history 失效逻辑;ChatHistory/AgentChat/AgentTimelineEvent 加入忽略集合 前端: 7. 新增时间线接口与类型定义 - frontend/src/api/schedule_agent.ts:新增 TimelineEvent/TimelineToolPayload/TimelineConfirmPayload 与 getConversationTimeline 8. AssistantPanel 全面对接 timeline 重建消息与卡片 - frontend/src/components/dashboard/AssistantPanel.vue:移除旧 history merge/normalize,新增 rebuildStateFromTimeline;支持 execution mode(always_execute);支持 resume-only 发送;修复 confirm 弹层手动关闭后重复弹出;会话标题显示放宽;流式中隐藏 action bar 9. 精排弹窗健壮性与交互动效优化 - frontend/src/components/assistant/ScheduleFineTuneModal.vue:previewData 支持 nullable,新增 visible 控制与 watch 初始化,补齐空值保护并调整弹窗动画 仓库: 10. 新增前端时间线接入说明文档 - docs/frontend/newagent_timeline_对接说明.md:接口、kind、payload、刷新重建与迁移建议
This commit is contained in:
@@ -260,13 +260,19 @@ watch([() => tasks.value.length, () => todayEvents.value.length, pageLoading], a
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<el-dialog v-model="createTaskDialogVisible" title="添加任务" width="460px" align-center class="dashboard-dialog">
|
||||
<el-dialog
|
||||
v-model="createTaskDialogVisible"
|
||||
title="添加新任务"
|
||||
width="440px"
|
||||
align-center
|
||||
class="dashboard-dialog premium-dialog"
|
||||
>
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="任务标题">
|
||||
<el-input v-model="taskForm.title" maxlength="255" placeholder="例如:完成数据库复习" />
|
||||
</el-form-item>
|
||||
<el-form-item label="优先级象限">
|
||||
<el-select v-model="taskForm.priority_group" class="dashboard-dialog__select">
|
||||
<el-select v-model="taskForm.priority_group" class="dashboard-dialog__select" popper-class="premium-select-popper" placement="bottom-start">
|
||||
<el-option :value="1" label="1 - 重要且紧急" />
|
||||
<el-option :value="2" label="2 - 重要不紧急" />
|
||||
<el-option :value="3" label="3 - 简单不重要" />
|
||||
@@ -274,12 +280,22 @@ watch([() => tasks.value.length, () => todayEvents.value.length, pageLoading], a
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="截止时间">
|
||||
<el-date-picker v-model="taskForm.deadline_at" type="datetime" placeholder="可选" class="dashboard-dialog__select" />
|
||||
<el-date-picker
|
||||
v-model="taskForm.deadline_at"
|
||||
type="datetime"
|
||||
placeholder="可选"
|
||||
class="dashboard-dialog__select"
|
||||
popper-class="premium-select-popper"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="createTaskDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="createTaskLoading" @click="handleCreateTask">保存任务</el-button>
|
||||
<div class="premium-dialog__footer">
|
||||
<button class="premium-btn premium-btn--ghost" @click="createTaskDialogVisible = false">取消</button>
|
||||
<button class="premium-btn premium-btn--primary" :disabled="createTaskLoading" @click="handleCreateTask">
|
||||
{{ createTaskLoading ? '保存中...' : '确认添加' }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
@@ -365,4 +381,196 @@ watch([() => tasks.value.length, () => todayEvents.value.length, pageLoading], a
|
||||
.dashboard-import__shape { position: absolute; right: -50px; bottom: -50px; width: 220px; height: 220px; opacity: 0.1; pointer-events: none; }
|
||||
.dashboard-import__shape-ring { position: absolute; inset: 0; border: 40px solid #3b82f6; border-radius: 50%; }
|
||||
.dashboard-import__shape-core { position: absolute; inset: 80px; background: #3b82f6; border-radius: 50%; }
|
||||
|
||||
/* --- Premium Dialog Styles --- */
|
||||
:global(.premium-dialog) {
|
||||
border-radius: 20px !important;
|
||||
background: #ffffff !important;
|
||||
border: 1px solid rgba(15, 23, 42, 0.08) !important;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25) !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(.premium-dialog .el-dialog__header) {
|
||||
padding: 24px 28px 12px !important;
|
||||
margin-right: 0 !important;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
:global(.premium-dialog .el-dialog__title) {
|
||||
font-size: 18px !important;
|
||||
font-weight: 800 !important;
|
||||
color: #0f172a !important;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
:global(.premium-dialog .el-dialog__body) {
|
||||
padding: 12px 28px 20px !important;
|
||||
}
|
||||
|
||||
:global(.premium-dialog .el-dialog__footer) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.premium-dialog__footer {
|
||||
padding: 16px 28px 24px;
|
||||
background: #f8fafc;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.premium-btn {
|
||||
height: 40px;
|
||||
padding: 0 20px;
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.premium-btn--primary {
|
||||
background: #3b82f6;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.premium-btn--primary:hover:not(:disabled) {
|
||||
background: #2563eb;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 16px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.premium-btn--ghost {
|
||||
background: #ffffff;
|
||||
color: #64748b;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.premium-btn--ghost:hover {
|
||||
background: #f1f5f9;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
/* 弹出动画覆写 */
|
||||
:global(.dialog-fade-enter-active .premium-dialog) {
|
||||
animation: premium-dialog-pop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) both;
|
||||
}
|
||||
|
||||
@keyframes premium-dialog-pop {
|
||||
0% { opacity: 0; transform: scale(0.92) translateY(20px); }
|
||||
60% { opacity: 1; transform: scale(1.02) translateY(-2px); }
|
||||
100% { opacity: 1; transform: scale(1) translateY(0); }
|
||||
}
|
||||
|
||||
:global(.el-overlay) {
|
||||
backdrop-filter: blur(6px);
|
||||
background: rgba(15, 23, 42, 0.35) !important;
|
||||
}
|
||||
|
||||
/* 表单美化 */
|
||||
:global(.premium-dialog .el-form-item__label) {
|
||||
font-weight: 700 !important;
|
||||
color: #475569 !important;
|
||||
font-size: 13px !important;
|
||||
margin-bottom: 6px !important;
|
||||
}
|
||||
|
||||
:global(.premium-dialog .el-input__wrapper),
|
||||
:global(.premium-dialog .el-select__wrapper) {
|
||||
background-color: #f8fafc !important;
|
||||
box-shadow: 0 0 0 1px #e2e8f0 inset !important;
|
||||
border-radius: 10px !important;
|
||||
padding: 4px 12px !important;
|
||||
}
|
||||
|
||||
:global(.premium-dialog .el-input__wrapper.is-focus),
|
||||
:global(.premium-dialog .el-select__wrapper.is-focused) {
|
||||
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2) inset !important;
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
:global(.premium-dialog .el-dialog__headerbtn) {
|
||||
top: 20px !important;
|
||||
right: 20px !important;
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
border-radius: 50% !important;
|
||||
background: #f1f5f9 !important;
|
||||
transition: all 0.2s !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
}
|
||||
|
||||
:global(.premium-dialog .el-dialog__headerbtn:hover) {
|
||||
background: #e2e8f0 !important;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
:global(.premium-dialog .el-dialog__headerbtn .el-dialog__close) {
|
||||
color: #64748b !important;
|
||||
font-size: 16px !important;
|
||||
font-weight: 800 !important;
|
||||
}
|
||||
|
||||
/* 统一输入框高度与背景 */
|
||||
:global(.premium-dialog .el-input__inner),
|
||||
:global(.premium-dialog .el-select .el-input__inner) {
|
||||
height: 38px !important;
|
||||
color: #0f172a !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
/* --- 下拉菜单扁平化 --- */
|
||||
:global(.premium-select-popper) {
|
||||
border-radius: 16px !important;
|
||||
border: 1px solid rgba(15, 23, 42, 0.08) !important;
|
||||
box-shadow: 0 12px 30px -5px rgba(0, 0, 0, 0.12) !important;
|
||||
background: #ffffff !important;
|
||||
overflow: hidden !important;
|
||||
margin-top: 8px !important;
|
||||
}
|
||||
|
||||
:global(.premium-select-popper .el-select-dropdown__list) {
|
||||
padding: 6px !important;
|
||||
}
|
||||
|
||||
:global(.premium-select-popper .el-select-dropdown__item) {
|
||||
border-radius: 10px !important;
|
||||
height: 38px !important;
|
||||
line-height: 38px !important;
|
||||
margin-bottom: 2px !important;
|
||||
font-weight: 600 !important;
|
||||
color: #475569 !important;
|
||||
padding: 0 12px !important;
|
||||
}
|
||||
|
||||
:global(.premium-select-popper .el-select-dropdown__item.is-selected) {
|
||||
background: #eff6ff !important;
|
||||
color: #3b82f6 !important;
|
||||
}
|
||||
|
||||
:global(.premium-select-popper .el-select-dropdown__item:hover) {
|
||||
background: #f1f5f9 !important;
|
||||
color: #0f172a !important;
|
||||
}
|
||||
|
||||
:global(.premium-select-popper .el-popper__arrow) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* 时间选择器特定深度覆盖 */
|
||||
:global(.premium-select-popper.el-picker-popper) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
:global(.premium-select-popper .el-picker-panel) {
|
||||
background: transparent !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user