import { ref } from 'vue' import { APPLICATION_TRANSPORT_MODE_OPTIONS, buildApplicationPreviewRows, buildLocalApplicationPreviewMessage, normalizeApplicationPreview, refreshApplicationPreviewTransportEstimate } from '../../utils/expenseApplicationPreview.js' import { waitForMockApplicationTransportQuote } from '../../utils/expenseApplicationEstimate.js' import { buildWorkbenchDateLabel, canApplyWorkbenchDateSelection, getTodayDateValue } from '../../utils/workbenchComposerDate.js' function parseEditorDateValue(value) { const text = String(value || '').trim() const matches = [...text.matchAll(/20\d{2}-\d{1,2}-\d{1,2}/g)].map((item) => item[0]) const startDate = matches[0] || getTodayDateValue() const endDate = matches[1] || startDate return { dateMode: matches.length > 1 && startDate !== endDate ? 'range' : 'single', singleDate: startDate, rangeStartDate: startDate, rangeEndDate: endDate } } function buildEmptyEditor() { return { messageId: '', fieldKey: '', draftValue: '', dateMode: 'single', singleDate: getTodayDateValue(), rangeStartDate: getTodayDateValue(), rangeEndDate: getTodayDateValue(), committing: false } } function shouldRefreshTransportEstimate(fieldKey) { return ['transportMode', 'time', 'location', 'days'].includes(fieldKey) } function buildTransportEstimatePendingPreview(preview = {}) { const fields = preview?.fields || {} return normalizeApplicationPreview({ ...preview, fields: { ...fields, transportPolicy: '正在预估交通费用...', policyEstimate: '正在同步费用测算...', transportEstimatedAmount: '查询中' } }) } export function useApplicationPreviewEditor({ persistSessionState, toast } = {}) { const applicationPreviewEditor = ref(buildEmptyEditor()) function resolveApplicationPreviewRows(message) { return buildApplicationPreviewRows(message?.applicationPreview || {}) } function resolveApplicationPreviewEditorControl(fieldKey) { if (fieldKey === 'transportMode') return 'select' if (fieldKey === 'time') return 'date' return 'text' } function resolveApplicationPreviewEditorOptions(fieldKey) { return fieldKey === 'transportMode' ? APPLICATION_TRANSPORT_MODE_OPTIONS : [] } function isApplicationPreviewEditing(message, fieldKey) { return ( String(applicationPreviewEditor.value.messageId || '') === String(message?.id || '') && applicationPreviewEditor.value.fieldKey === fieldKey ) } function openApplicationPreviewEditor(message, fieldKey, value) { if (!message?.applicationPreview || !fieldKey) return const targetRow = buildApplicationPreviewRows(message.applicationPreview) .find((row) => row.key === fieldKey) if (targetRow && targetRow.editable === false) return const normalizedValue = String(value || '').trim() === '待补充' ? '' : String(value || '') const dateState = fieldKey === 'time' ? parseEditorDateValue(normalizedValue) : {} applicationPreviewEditor.value = { messageId: String(message.id || ''), fieldKey, draftValue: fieldKey === 'transportMode' && !APPLICATION_TRANSPORT_MODE_OPTIONS.includes(normalizedValue) ? '' : normalizedValue, committing: false, ...dateState } } function cancelApplicationPreviewEditor() { applicationPreviewEditor.value = buildEmptyEditor() } function isApplicationPreviewDateEditorOpen(message) { return isApplicationPreviewEditing(message, 'time') } function setApplicationPreviewDateMode(mode) { applicationPreviewEditor.value.dateMode = mode === 'range' ? 'range' : 'single' } function canApplyApplicationPreviewDateSelection() { const editor = applicationPreviewEditor.value return canApplyWorkbenchDateSelection({ mode: editor.dateMode, singleDate: editor.singleDate, rangeStartDate: editor.rangeStartDate, rangeEndDate: editor.rangeEndDate }) } function buildApplicationPreviewDateDraftValue() { const editor = applicationPreviewEditor.value return buildWorkbenchDateLabel({ mode: editor.dateMode, singleDate: editor.singleDate, rangeStartDate: editor.rangeStartDate, rangeEndDate: editor.rangeEndDate }) } async function commitApplicationPreviewEditor(message) { const editor = applicationPreviewEditor.value if (editor.committing) { return false } if (!message?.applicationPreview || String(editor.messageId || '') !== String(message.id || '') || !editor.fieldKey) { cancelApplicationPreviewEditor() return false } applicationPreviewEditor.value = { ...editor, committing: true } const nextValue = editor.fieldKey === 'time' ? buildApplicationPreviewDateDraftValue() : String(editor.draftValue || '').trim() if (editor.fieldKey === 'time' && !nextValue) { toast?.('请先选择有效日期。') applicationPreviewEditor.value = { ...applicationPreviewEditor.value, committing: false } return false } const nextPreview = normalizeApplicationPreview({ ...message.applicationPreview, fields: { ...(message.applicationPreview.fields || {}), [editor.fieldKey]: nextValue } }) const needRefreshTransport = shouldRefreshTransportEstimate(editor.fieldKey) && String(nextPreview.fields?.transportMode || '').trim() message.applicationPreview = needRefreshTransport ? buildTransportEstimatePendingPreview(nextPreview) : nextPreview message.text = buildLocalApplicationPreviewMessage(message.applicationPreview) cancelApplicationPreviewEditor() persistSessionState?.() if (needRefreshTransport) { await waitForMockApplicationTransportQuote({ transportMode: nextPreview.fields.transportMode, location: nextPreview.fields.matchedCity || nextPreview.fields.location, time: nextPreview.fields.time }) const refreshedPreview = refreshApplicationPreviewTransportEstimate(nextPreview) message.applicationPreview = refreshedPreview message.text = buildLocalApplicationPreviewMessage(refreshedPreview) persistSessionState?.() toast?.('已更新出行方式和费用测算。') return true } toast?.('已更新核对表内容。') return true } async function commitApplicationPreviewDateEditor(message) { if (!canApplyApplicationPreviewDateSelection()) { toast?.('请确认结束日期不早于开始日期。') return false } return commitApplicationPreviewEditor(message) } function handleApplicationPreviewEditorKeydown(event, message) { if (event.key === 'Enter') { event.preventDefault() commitApplicationPreviewEditor(message) return } if (event.key === 'Escape') { event.preventDefault() cancelApplicationPreviewEditor() } } return { applicationPreviewEditor, resolveApplicationPreviewRows, resolveApplicationPreviewEditorControl, resolveApplicationPreviewEditorOptions, isApplicationPreviewEditing, isApplicationPreviewDateEditorOpen, openApplicationPreviewEditor, commitApplicationPreviewEditor, commitApplicationPreviewDateEditor, cancelApplicationPreviewEditor, setApplicationPreviewDateMode, canApplyApplicationPreviewDateSelection, handleApplicationPreviewEditorKeydown } }