feat: add quick action buttons (preview/undo/reset) to sidebar

Sidebar now has 快捷操作 section matching Streamlit app functionality:
- 预览 — sends "预览报表" to preview current JRXML
- 撤销 — sends "撤销上一步修改" to revert last change
- 重置 — sends "重新来,清空当前报表" to reset session

Session store now tracks history_states for undo availability check.
This commit is contained in:
2026-05-21 23:54:57 +08:00
parent a364e1de81
commit d600cbf285
3 changed files with 99 additions and 3 deletions
+1 -1
View File
@@ -114,7 +114,7 @@ async function handleSend(text: string, files: File[]) {
<template> <template>
<div class="app-layout"> <div class="app-layout">
<Sidebar /> <Sidebar @quickAction="(text) => handleSend(text, [])" />
<main class="main-area"> <main class="main-area">
<div class="chat-container" ref="chatContainer"> <div class="chat-container" ref="chatContainer">
+90
View File
@@ -6,6 +6,22 @@ import { useChatStore } from '../stores/chat'
const session = useSessionStore() const session = useSessionStore()
const chat = useChatStore() const chat = useChatStore()
const emit = defineEmits<{
quickAction: [text: string]
}>()
function handlePreview() {
emit('quickAction', '预览报表')
}
function handleUndo() {
emit('quickAction', '撤销上一步修改')
}
function handleReset() {
emit('quickAction', '重新来,清空当前报表')
}
onMounted(() => { onMounted(() => {
session.loadSessions() session.loadSessions()
}) })
@@ -63,6 +79,26 @@ async function handleDelete() {
</button> </button>
</div> </div>
<div class="sidebar-section" v-if="session.currentId">
<div class="section-title">快捷操作</div>
<div class="quick-actions">
<button
class="btn-action btn-preview"
:disabled="!session.hasJrxml"
@click="handlePreview"
>预览</button>
<button
class="btn-action btn-undo"
:disabled="!session.hasHistory"
@click="handleUndo"
>撤销</button>
<button
class="btn-action btn-reset"
@click="handleReset"
>重置</button>
</div>
</div>
<div class="sidebar-section" v-if="session.currentJrxml || session.versions.length > 0"> <div class="sidebar-section" v-if="session.currentJrxml || session.versions.length > 0">
<div class="section-title">下载</div> <div class="section-title">下载</div>
<a <a
@@ -207,6 +243,60 @@ async function handleDelete() {
color: #1e1e2e; color: #1e1e2e;
} }
.quick-actions {
display: flex;
gap: 6px;
padding: 0 16px;
}
.btn-action {
flex: 1;
padding: 6px 8px;
border: 1px solid #45475a;
background: #313244;
color: #cdd6f4;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
transition: background 0.15s, border-color 0.15s;
}
.btn-action:hover:not(:disabled) {
background: #45475a;
}
.btn-action:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.btn-preview {
border-color: #89b4fa;
}
.btn-preview:hover:not(:disabled) {
background: #89b4fa;
color: #1e1e2e;
}
.btn-undo {
border-color: #f9e2af;
}
.btn-undo:hover:not(:disabled) {
background: #f9e2af;
color: #1e1e2e;
}
.btn-reset {
border-color: #f38ba8;
}
.btn-reset:hover:not(:disabled) {
background: #f38ba8;
color: #1e1e2e;
}
.btn-download { .btn-download {
display: block; display: block;
padding: 8px 16px; padding: 8px 16px;
+8 -2
View File
@@ -9,8 +9,12 @@ export const useSessionStore = defineStore('session', () => {
const currentId = ref<string>('') const currentId = ref<string>('')
const currentName = ref<string>('') const currentName = ref<string>('')
const versions = ref<any[]>([]) const versions = ref<any[]>([])
const historyStates = ref<any[]>([])
const currentJrxml = ref<string>('') const currentJrxml = ref<string>('')
const hasJrxml = computed(() => !!currentJrxml.value)
const hasHistory = computed(() => historyStates.value.length > 0)
const sortedSessions = computed(() => const sortedSessions = computed(() =>
[...sessions.value].sort((a, b) => [...sessions.value].sort((a, b) =>
new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime() new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
@@ -43,6 +47,7 @@ export const useSessionStore = defineStore('session', () => {
const state = data.agent_state const state = data.agent_state
currentJrxml.value = state.current_jrxml || '' currentJrxml.value = state.current_jrxml || ''
versions.value = state.jrxml_versions || [] versions.value = state.jrxml_versions || []
historyStates.value = state.history_states || []
} catch (e) { } catch (e) {
console.error('加载会话失败:', e) console.error('加载会话失败:', e)
} }
@@ -61,11 +66,12 @@ export const useSessionStore = defineStore('session', () => {
function refreshFromState(agentState: Record<string, any>) { function refreshFromState(agentState: Record<string, any>) {
currentJrxml.value = agentState.current_jrxml || currentJrxml.value currentJrxml.value = agentState.current_jrxml || currentJrxml.value
versions.value = agentState.jrxml_versions || versions.value versions.value = agentState.jrxml_versions || versions.value
historyStates.value = agentState.history_states || historyStates.value
} }
return { return {
sessions, currentId, currentName, versions, currentJrxml, sessions, currentId, currentName, versions, historyStates, currentJrxml,
sortedSessions, currentSession, hasJrxml, hasHistory, sortedSessions, currentSession,
loadSessions, createSession, switchSession, deleteCurrent, refreshFromState, loadSessions, createSession, switchSession, deleteCurrent, refreshFromState,
} }
}) })