feat: 完善文档中心与报销申请交互及侧边栏重构
后端优化编排器报销查询和本体检测精度,增强报销单草稿保 存和附件回填逻辑,前端重构侧边栏组件支持折叠和图标导 航,完善文档中心状态筛选和详情提示,报销创建和审批详情 页优化会话管理和费用明细交互,新增助手应用服务和预设动 作工具函数,补充单元测试覆盖。
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { nextTick, ref } from 'vue'
|
||||
|
||||
import { clearUserConversations, fetchLatestConversation } from '../../services/orchestrator.js'
|
||||
import { fetchLatestConversation } from '../../services/orchestrator.js'
|
||||
import {
|
||||
clearAssistantSessionSnapshot,
|
||||
readAssistantSessionSnapshot,
|
||||
@@ -11,8 +11,9 @@ import {
|
||||
filterPersistableFilePreviews
|
||||
} from './travelReimbursementAttachmentModel.js'
|
||||
import {
|
||||
ASSISTANT_SESSION_TYPES,
|
||||
SESSION_TYPE_APPLICATION,
|
||||
SESSION_TYPE_EXPENSE,
|
||||
SESSION_TYPE_KNOWLEDGE,
|
||||
buildInitialInsightFromConversation,
|
||||
buildWelcomeInsight,
|
||||
buildWelcomeQuickActions,
|
||||
@@ -20,6 +21,7 @@ import {
|
||||
hasMeaningfulSessionMessages,
|
||||
normalizeInitialConversationMessages,
|
||||
normalizeSnapshotMessages,
|
||||
normalizeAssistantSessionType,
|
||||
resolveInitialConversationId,
|
||||
resolveInitialDraftClaimId,
|
||||
resolveInitialSessionType,
|
||||
@@ -41,6 +43,10 @@ export function useTravelReimbursementSessionState({
|
||||
scrollToBottom,
|
||||
getSessionRuntimeRefs = () => ({})
|
||||
}) {
|
||||
function resolveDefaultSessionTypeFromEntry() {
|
||||
return props.entrySource === 'application' ? SESSION_TYPE_APPLICATION : SESSION_TYPE_EXPENSE
|
||||
}
|
||||
|
||||
function refreshWelcomeQuickActions(messages, sessionType) {
|
||||
if (!Array.isArray(messages) || !messages.length) {
|
||||
return []
|
||||
@@ -58,8 +64,8 @@ export function useTravelReimbursementSessionState({
|
||||
))
|
||||
}
|
||||
|
||||
function buildConversationSessionState(conversation, fallbackSessionType = SESSION_TYPE_EXPENSE) {
|
||||
const sessionType = resolveInitialSessionType(conversation) || fallbackSessionType
|
||||
function buildConversationSessionState(conversation, fallbackSessionType = resolveDefaultSessionTypeFromEntry()) {
|
||||
const sessionType = resolveInitialSessionType(conversation, fallbackSessionType)
|
||||
const restoredMessages = refreshWelcomeQuickActions(normalizeInitialConversationMessages(conversation), sessionType)
|
||||
const initialInsight = buildInitialInsightFromConversation(conversation)
|
||||
const restoredReviewFilePreviews = buildReviewFilePreviewsFromMessages(restoredMessages)
|
||||
@@ -84,17 +90,18 @@ export function useTravelReimbursementSessionState({
|
||||
}
|
||||
|
||||
function buildEmptySessionState(sessionType) {
|
||||
const normalizedSessionType = normalizeAssistantSessionType(sessionType, resolveDefaultSessionTypeFromEntry())
|
||||
return {
|
||||
sessionType,
|
||||
sessionType: normalizedSessionType,
|
||||
messages: [
|
||||
createWelcomeAssistantMessage(props.entrySource, linkedRequest.value, sessionType, currentUser.value)
|
||||
createWelcomeAssistantMessage(props.entrySource, linkedRequest.value, normalizedSessionType, currentUser.value)
|
||||
],
|
||||
conversationId: '',
|
||||
draftClaimId: '',
|
||||
currentInsight: buildWelcomeInsight(
|
||||
props.entrySource,
|
||||
linkedRequest.value,
|
||||
sessionType,
|
||||
normalizedSessionType,
|
||||
currentUser.value
|
||||
),
|
||||
reviewFilePreviews: [],
|
||||
@@ -107,13 +114,16 @@ export function useTravelReimbursementSessionState({
|
||||
}
|
||||
}
|
||||
|
||||
function buildPersistedSessionState(snapshot, fallbackSessionType = SESSION_TYPE_EXPENSE) {
|
||||
function buildPersistedSessionState(snapshot, fallbackSessionType = resolveDefaultSessionTypeFromEntry()) {
|
||||
const state = snapshot?.state && typeof snapshot.state === 'object' ? snapshot.state : null
|
||||
if (!state) {
|
||||
return null
|
||||
}
|
||||
|
||||
const sessionType = String(state.sessionType || snapshot.sessionType || fallbackSessionType || '').trim() || SESSION_TYPE_EXPENSE
|
||||
const sessionType = normalizeAssistantSessionType(
|
||||
state.sessionType || snapshot.sessionType || fallbackSessionType,
|
||||
fallbackSessionType
|
||||
)
|
||||
const restoredMessages = refreshWelcomeQuickActions(normalizeSnapshotMessages(state.messages), sessionType)
|
||||
if (
|
||||
!hasMeaningfulSessionMessages(restoredMessages)
|
||||
@@ -148,13 +158,16 @@ export function useTravelReimbursementSessionState({
|
||||
return String(user.username || user.name || 'anonymous').trim() || 'anonymous'
|
||||
}
|
||||
|
||||
const initialSessionType = resolveInitialSessionType(props.initialConversation)
|
||||
const defaultInitialSessionType = resolveDefaultSessionTypeFromEntry()
|
||||
const initialSessionType = props.initialConversation
|
||||
? resolveInitialSessionType(props.initialConversation, defaultInitialSessionType)
|
||||
: defaultInitialSessionType
|
||||
const shouldPersistLocalSnapshot = props.entrySource !== 'detail'
|
||||
const conversationInitialState = props.initialConversation
|
||||
? buildConversationSessionState(props.initialConversation, initialSessionType)
|
||||
: buildEmptySessionState(initialSessionType)
|
||||
const canRestorePersistedInitialState =
|
||||
props.entrySource === 'workbench'
|
||||
shouldPersistLocalSnapshot
|
||||
&& !String(props.initialPrompt || '').trim()
|
||||
&& !props.initialFiles.length
|
||||
const persistedInitialSnapshot = readAssistantSessionSnapshot(resolveCurrentUserId(), initialSessionType)
|
||||
@@ -174,21 +187,22 @@ export function useTravelReimbursementSessionState({
|
||||
const conversationId = ref(initialSessionState.conversationId)
|
||||
const draftClaimId = ref(initialSessionState.draftClaimId)
|
||||
const reviewFilePreviews = ref(initialSessionState.reviewFilePreviews)
|
||||
const sessionSnapshots = ref({
|
||||
[SESSION_TYPE_EXPENSE]: null,
|
||||
[SESSION_TYPE_KNOWLEDGE]: null
|
||||
})
|
||||
const sessionSnapshots = ref(
|
||||
ASSISTANT_SESSION_TYPES.reduce((result, sessionType) => {
|
||||
result[sessionType] = null
|
||||
return result
|
||||
}, {})
|
||||
)
|
||||
const currentInsight = ref(initialSessionState.currentInsight)
|
||||
const composerUploadIntent = ref(String(initialSessionState.composerUploadIntent || '').trim())
|
||||
const guidedFlowState = ref(normalizeGuidedFlowState(initialSessionState.guidedFlowState))
|
||||
const insightPanelCollapsed = ref(false)
|
||||
const sessionSwitchBusy = ref(false)
|
||||
let knowledgeSessionResetPromise = Promise.resolve()
|
||||
|
||||
function buildPersistableSessionState(sessionState) {
|
||||
const state = sessionState || captureCurrentSessionState()
|
||||
return {
|
||||
sessionType: state.sessionType || SESSION_TYPE_EXPENSE,
|
||||
sessionType: normalizeAssistantSessionType(state.sessionType, resolveDefaultSessionTypeFromEntry()),
|
||||
messages: serializeSessionMessages(state.messages),
|
||||
conversationId: String(state.conversationId || '').trim(),
|
||||
draftClaimId: String(state.draftClaimId || '').trim(),
|
||||
@@ -244,7 +258,7 @@ export function useTravelReimbursementSessionState({
|
||||
function applySessionState(sessionState) {
|
||||
const runtimeRefs = getSessionRuntimeRefs()
|
||||
const nextState = sessionState || buildEmptySessionState(activeSessionType.value)
|
||||
activeSessionType.value = nextState.sessionType || SESSION_TYPE_EXPENSE
|
||||
activeSessionType.value = normalizeAssistantSessionType(nextState.sessionType, resolveDefaultSessionTypeFromEntry())
|
||||
messages.value = Array.isArray(nextState.messages) && nextState.messages.length
|
||||
? nextState.messages
|
||||
: [
|
||||
@@ -287,38 +301,18 @@ export function useTravelReimbursementSessionState({
|
||||
}
|
||||
|
||||
async function loadLatestSessionState(targetSessionType) {
|
||||
const payload = await fetchLatestConversation(resolveCurrentUserId(), targetSessionType, {
|
||||
preferRecoverable: targetSessionType === SESSION_TYPE_EXPENSE
|
||||
const normalizedTarget = normalizeAssistantSessionType(targetSessionType, resolveDefaultSessionTypeFromEntry())
|
||||
const payload = await fetchLatestConversation(resolveCurrentUserId(), normalizedTarget, {
|
||||
preferRecoverable: normalizedTarget === SESSION_TYPE_EXPENSE
|
||||
})
|
||||
if (payload?.found && payload.conversation) {
|
||||
return buildConversationSessionState(payload.conversation, targetSessionType)
|
||||
return buildConversationSessionState(payload.conversation, normalizedTarget)
|
||||
}
|
||||
return buildEmptySessionState(targetSessionType)
|
||||
}
|
||||
|
||||
function resetKnowledgeSessionSnapshot() {
|
||||
const emptyKnowledgeState = buildEmptySessionState(SESSION_TYPE_KNOWLEDGE)
|
||||
sessionSnapshots.value[SESSION_TYPE_KNOWLEDGE] = emptyKnowledgeState
|
||||
|
||||
if (activeSessionType.value === SESSION_TYPE_KNOWLEDGE) {
|
||||
applySessionState(emptyKnowledgeState)
|
||||
}
|
||||
}
|
||||
|
||||
function clearKnowledgeSessionOnEntry() {
|
||||
resetKnowledgeSessionSnapshot()
|
||||
knowledgeSessionResetPromise = clearUserConversations(resolveCurrentUserId(), SESSION_TYPE_KNOWLEDGE)
|
||||
.catch((error) => {
|
||||
console.warn('Failed to clear knowledge session on entry:', error)
|
||||
})
|
||||
.finally(() => {
|
||||
resetKnowledgeSessionSnapshot()
|
||||
})
|
||||
return knowledgeSessionResetPromise
|
||||
return buildEmptySessionState(normalizedTarget)
|
||||
}
|
||||
|
||||
async function switchSessionType(targetSessionType) {
|
||||
const normalizedTarget = String(targetSessionType || '').trim() || SESSION_TYPE_EXPENSE
|
||||
const normalizedTarget = normalizeAssistantSessionType(targetSessionType, resolveDefaultSessionTypeFromEntry())
|
||||
if (normalizedTarget === activeSessionType.value || sessionSwitchBusy.value) {
|
||||
return
|
||||
}
|
||||
@@ -338,7 +332,7 @@ export function useTravelReimbursementSessionState({
|
||||
const emptyState = buildEmptySessionState(normalizedTarget)
|
||||
sessionSnapshots.value[normalizedTarget] = emptyState
|
||||
applySessionState(emptyState)
|
||||
toast(error?.message || '?????????????????')
|
||||
toast(error?.message || '切换助手失败,请稍后重试。')
|
||||
} finally {
|
||||
sessionSwitchBusy.value = false
|
||||
}
|
||||
@@ -368,8 +362,6 @@ export function useTravelReimbursementSessionState({
|
||||
captureCurrentSessionState,
|
||||
applySessionState,
|
||||
loadLatestSessionState,
|
||||
resetKnowledgeSessionSnapshot,
|
||||
clearKnowledgeSessionOnEntry,
|
||||
switchSessionType
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user