2026-06-13 14:52:26 +00:00
|
|
|
import { nextTick, onBeforeUnmount, onMounted, watch } from 'vue'
|
|
|
|
|
import {
|
|
|
|
|
buildLocalApplicationPreviewMessage,
|
|
|
|
|
normalizeApplicationPreview
|
|
|
|
|
} from '../../utils/expenseApplicationPreview.js'
|
|
|
|
|
import {
|
|
|
|
|
MAX_ATTACHMENTS,
|
|
|
|
|
VISIBLE_ATTACHMENT_CHIPS,
|
|
|
|
|
buildReviewFilePreviewsFromReviewPayload
|
|
|
|
|
} from './travelReimbursementAttachmentModel.js'
|
|
|
|
|
import {
|
|
|
|
|
SESSION_TYPE_EXPENSE,
|
|
|
|
|
createMessage,
|
|
|
|
|
buildWelcomeInsight
|
|
|
|
|
} from './travelReimbursementConversationModel.js'
|
|
|
|
|
|
|
|
|
|
export function useTravelReimbursementCreateViewLifecycle({
|
|
|
|
|
activeFlowSteps,
|
|
|
|
|
activeReviewPanelScope,
|
|
|
|
|
activeReviewPayload,
|
|
|
|
|
activeSessionType,
|
|
|
|
|
adjustComposerTextareaHeight,
|
|
|
|
|
attachedFiles,
|
|
|
|
|
clearExpenseSessionForDeletedClaim,
|
|
|
|
|
clearStewardThinkingTimers,
|
|
|
|
|
closeAfterBusy,
|
|
|
|
|
composerDraft,
|
|
|
|
|
composerFilesExpanded,
|
|
|
|
|
composerUploadIntent,
|
|
|
|
|
conversationId,
|
|
|
|
|
currentInsight,
|
|
|
|
|
currentUser,
|
|
|
|
|
draftClaimId,
|
|
|
|
|
guidedFlowState,
|
|
|
|
|
handleComposerDatePickerOutside,
|
|
|
|
|
hasInsightPanelContent,
|
|
|
|
|
insightPanelCollapsed,
|
|
|
|
|
linkedRequest,
|
|
|
|
|
maybeFinalizeDeferredClose,
|
|
|
|
|
mergeFilesWithLimit,
|
|
|
|
|
messages,
|
|
|
|
|
persistSessionState,
|
|
|
|
|
props,
|
|
|
|
|
rememberFilePreviews,
|
|
|
|
|
resetReviewDrawerFromPayload,
|
|
|
|
|
resolveActiveClaimId,
|
|
|
|
|
restorePersistedDraftAttachmentPreviews,
|
2026-06-22 11:58:53 +08:00
|
|
|
reviewActionBusy,
|
2026-06-13 14:52:26 +00:00
|
|
|
reviewDocumentDrawerAvailable,
|
|
|
|
|
reviewDrawerMode,
|
|
|
|
|
reviewFilePreviews,
|
|
|
|
|
reviewFlowDrawerAvailable,
|
|
|
|
|
reviewRiskDrawerAvailable,
|
|
|
|
|
scrollToBottom,
|
|
|
|
|
startFlowTick,
|
2026-06-22 11:58:53 +08:00
|
|
|
stewardState,
|
2026-06-13 14:52:26 +00:00
|
|
|
stopAttachmentRuntime,
|
|
|
|
|
stopFlowRuntime,
|
|
|
|
|
submitComposer,
|
2026-06-22 11:58:53 +08:00
|
|
|
submitting,
|
|
|
|
|
sessionSwitchBusy,
|
2026-06-13 14:52:26 +00:00
|
|
|
toast,
|
|
|
|
|
workbenchVisible,
|
|
|
|
|
REVIEW_DRAWER_MODE_DOCUMENTS,
|
|
|
|
|
REVIEW_DRAWER_MODE_FLOW,
|
|
|
|
|
REVIEW_DRAWER_MODE_REVIEW,
|
|
|
|
|
REVIEW_DRAWER_MODE_RISK,
|
|
|
|
|
SESSION_TYPE_EXPENSE: sessionTypeExpense = SESSION_TYPE_EXPENSE
|
|
|
|
|
}) {
|
|
|
|
|
watch(
|
|
|
|
|
() => [activeReviewPayload.value, activeReviewPanelScope.value],
|
|
|
|
|
([payload]) => {
|
|
|
|
|
rememberFilePreviews(buildReviewFilePreviewsFromReviewPayload(payload))
|
|
|
|
|
const shouldKeepFlowDrawer = reviewDrawerMode.value === REVIEW_DRAWER_MODE_FLOW && activeFlowSteps.value.length > 0
|
|
|
|
|
resetReviewDrawerFromPayload(payload)
|
|
|
|
|
if (shouldKeepFlowDrawer) {
|
|
|
|
|
reviewDrawerMode.value = REVIEW_DRAWER_MODE_FLOW
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => hasInsightPanelContent.value,
|
|
|
|
|
(available) => {
|
|
|
|
|
if (!available) {
|
|
|
|
|
insightPanelCollapsed.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => reviewDocumentDrawerAvailable.value,
|
|
|
|
|
(available) => {
|
|
|
|
|
if (!available && reviewDrawerMode.value === REVIEW_DRAWER_MODE_DOCUMENTS) {
|
|
|
|
|
reviewDrawerMode.value = REVIEW_DRAWER_MODE_REVIEW
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => reviewRiskDrawerAvailable.value,
|
|
|
|
|
(available) => {
|
|
|
|
|
if (!available && reviewDrawerMode.value === REVIEW_DRAWER_MODE_RISK) {
|
|
|
|
|
reviewDrawerMode.value = REVIEW_DRAWER_MODE_REVIEW
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => reviewFlowDrawerAvailable.value,
|
|
|
|
|
(available) => {
|
|
|
|
|
if (!available && reviewDrawerMode.value === REVIEW_DRAWER_MODE_FLOW) {
|
|
|
|
|
reviewDrawerMode.value = REVIEW_DRAWER_MODE_REVIEW
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => [activeSessionType.value, activeFlowSteps.value.length],
|
|
|
|
|
([, activeCount], [, previousActiveCount] = []) => {
|
|
|
|
|
if (activeCount <= 0 || previousActiveCount > 0) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
reviewDrawerMode.value = REVIEW_DRAWER_MODE_FLOW
|
|
|
|
|
insightPanelCollapsed.value = false
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => composerDraft.value,
|
|
|
|
|
() => {
|
|
|
|
|
nextTick(adjustComposerTextareaHeight)
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => ({
|
|
|
|
|
sessionType: activeSessionType.value,
|
|
|
|
|
conversationId: conversationId.value,
|
2026-06-22 11:58:53 +08:00
|
|
|
stewardState: stewardState.value,
|
2026-06-13 14:52:26 +00:00
|
|
|
draftClaimId: draftClaimId.value,
|
|
|
|
|
messages: messages.value,
|
|
|
|
|
currentInsight: currentInsight.value,
|
|
|
|
|
reviewFilePreviews: reviewFilePreviews.value,
|
|
|
|
|
composerDraft: composerDraft.value,
|
|
|
|
|
composerUploadIntent: composerUploadIntent.value,
|
|
|
|
|
guidedFlowState: guidedFlowState.value,
|
|
|
|
|
insightPanelCollapsed: insightPanelCollapsed.value
|
|
|
|
|
}),
|
|
|
|
|
() => {
|
|
|
|
|
persistSessionState()
|
|
|
|
|
},
|
|
|
|
|
{ deep: true }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => [activeSessionType.value, resolveActiveClaimId()],
|
|
|
|
|
([sessionType, claimId]) => {
|
|
|
|
|
if (sessionType !== sessionTypeExpense || !claimId) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
void restorePersistedDraftAttachmentPreviews(claimId)
|
|
|
|
|
},
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => props.invalidatedDraftClaimId,
|
|
|
|
|
(claimId) => {
|
|
|
|
|
clearExpenseSessionForDeletedClaim(claimId)
|
|
|
|
|
},
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => workbenchVisible.value,
|
|
|
|
|
(visible) => {
|
|
|
|
|
if (visible) {
|
|
|
|
|
scrollToBottom()
|
|
|
|
|
} else {
|
|
|
|
|
maybeFinalizeDeferredClose()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
2026-06-22 11:58:53 +08:00
|
|
|
watch(
|
|
|
|
|
() => [submitting.value, reviewActionBusy.value, sessionSwitchBusy.value, workbenchVisible.value],
|
|
|
|
|
() => {
|
|
|
|
|
maybeFinalizeDeferredClose()
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
2026-06-13 14:52:26 +00:00
|
|
|
watch(
|
|
|
|
|
() => props.reopenToken,
|
|
|
|
|
(token, previousToken) => {
|
|
|
|
|
if (token === previousToken) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
closeAfterBusy.value = false
|
|
|
|
|
workbenchVisible.value = true
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
adjustComposerTextareaHeight()
|
|
|
|
|
scrollToBottom()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
document.addEventListener('click', handleComposerDatePickerOutside)
|
|
|
|
|
startFlowTick()
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
workbenchVisible.value = true
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
adjustComposerTextareaHeight()
|
|
|
|
|
scrollToBottom()
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
currentInsight.value =
|
|
|
|
|
currentInsight.value
|
|
|
|
|
|| buildWelcomeInsight(props.entrySource, linkedRequest.value, activeSessionType.value, currentUser.value)
|
|
|
|
|
if (props.initialApplicationPreview && typeof props.initialApplicationPreview === 'object') {
|
|
|
|
|
const applicationPreview = normalizeApplicationPreview(props.initialApplicationPreview)
|
|
|
|
|
messages.value.push(createMessage('assistant', buildLocalApplicationPreviewMessage(applicationPreview), [], {
|
|
|
|
|
meta: ['修改申请'],
|
|
|
|
|
applicationPreview
|
|
|
|
|
}))
|
|
|
|
|
persistSessionState()
|
|
|
|
|
}
|
|
|
|
|
if (props.initialPrompt?.trim() || props.initialFiles.length) {
|
|
|
|
|
const initialMerge = mergeFilesWithLimit([], Array.from(props.initialFiles), MAX_ATTACHMENTS)
|
|
|
|
|
composerDraft.value = props.initialPrompt.trim()
|
|
|
|
|
attachedFiles.value = initialMerge.files
|
|
|
|
|
composerFilesExpanded.value = initialMerge.files.length > VISIBLE_ATTACHMENT_CHIPS
|
|
|
|
|
if (initialMerge.overflowCount > 0) {
|
|
|
|
|
toast(`一次最多上传 ${MAX_ATTACHMENTS} 份附件,已保留前 ${MAX_ATTACHMENTS} 份。`)
|
|
|
|
|
}
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
adjustComposerTextareaHeight()
|
|
|
|
|
})
|
|
|
|
|
if (props.initialPromptAutoSubmit !== false) {
|
|
|
|
|
submitComposer()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
document.removeEventListener('click', handleComposerDatePickerOutside)
|
|
|
|
|
clearStewardThinkingTimers()
|
|
|
|
|
stopFlowRuntime()
|
|
|
|
|
stopAttachmentRuntime()
|
|
|
|
|
})
|
|
|
|
|
}
|