feat: 同步报销流程与工作台改动
This commit is contained in:
@@ -5,7 +5,7 @@ import { useNavigation, navItems } from './useNavigation.js'
|
||||
import { mapExpenseClaimToRequest, useRequests } from './useRequests.js'
|
||||
import { useSystemState } from './useSystemState.js'
|
||||
import { useToast } from './useToast.js'
|
||||
import { fetchExpenseClaimDetail } from '../services/reimbursements.js'
|
||||
import { fetchAllApprovalExpenseClaims, fetchExpenseClaimDetail } from '../services/reimbursements.js'
|
||||
import { fetchOntologyParse } from '../services/ontology.js'
|
||||
import { fetchLatestConversation } from '../services/orchestrator.js'
|
||||
import { clearAssistantSessionSnapshotForDraftClaim } from '../utils/assistantSessionSnapshot.js'
|
||||
@@ -18,20 +18,20 @@ import {
|
||||
} from '../utils/workbenchAssistantIntent.js'
|
||||
import { buildWorkbenchSummary } from '../utils/workbenchSummary.js'
|
||||
import { createCurrentYearDateRange } from '../utils/dateRangeDefaults.js'
|
||||
|
||||
|
||||
const SESSION_TYPE_EXPENSE = 'expense'
|
||||
const SMART_ENTRY_SOURCE_APPLICATION = 'application'
|
||||
const SMART_ENTRY_SOURCE_REIMBURSEMENT = 'topbar'
|
||||
|
||||
export function useAppShell() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const smartEntryOpen = ref(false)
|
||||
const smartEntryContext = ref({
|
||||
prompt: '',
|
||||
|
||||
export function useAppShell() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const smartEntryOpen = ref(false)
|
||||
const smartEntryContext = ref({
|
||||
prompt: '',
|
||||
source: 'documents',
|
||||
request: null,
|
||||
request: null,
|
||||
files: [],
|
||||
conversation: null,
|
||||
scope: null,
|
||||
@@ -45,16 +45,17 @@ export function useAppShell() {
|
||||
const smartEntryInvalidatedDraftClaimId = ref('')
|
||||
const selectedRequestSnapshot = ref(null)
|
||||
const documentCenterRefreshToken = ref(0)
|
||||
|
||||
const { activeView, currentView, setView } = useNavigation()
|
||||
const {
|
||||
requests,
|
||||
loading: requestsLoading,
|
||||
error: requestsError,
|
||||
search,
|
||||
filters,
|
||||
ranges,
|
||||
activeRange,
|
||||
const workbenchApprovalRequests = ref([])
|
||||
|
||||
const { activeView, currentView, setView } = useNavigation()
|
||||
const {
|
||||
requests,
|
||||
loading: requestsLoading,
|
||||
error: requestsError,
|
||||
search,
|
||||
filters,
|
||||
ranges,
|
||||
activeRange,
|
||||
filteredRequests,
|
||||
approveRequest,
|
||||
rejectRequest,
|
||||
@@ -65,7 +66,7 @@ export function useAppShell() {
|
||||
const { toast } = useToast()
|
||||
|
||||
const customRange = ref(createCurrentYearDateRange())
|
||||
|
||||
|
||||
const selectedRequest = computed(() => {
|
||||
const requestId = String(route.params.requestId || '')
|
||||
|
||||
@@ -105,6 +106,40 @@ export function useAppShell() {
|
||||
return reloadRequests()
|
||||
}
|
||||
|
||||
async function reloadWorkbenchApprovalRequests() {
|
||||
try {
|
||||
const payload = await fetchAllApprovalExpenseClaims()
|
||||
workbenchApprovalRequests.value = Array.isArray(payload)
|
||||
? payload.map((item) => mapExpenseClaimToRequest(item))
|
||||
: []
|
||||
} catch {
|
||||
workbenchApprovalRequests.value = []
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadWorkbenchRequests() {
|
||||
const [payload] = await Promise.all([
|
||||
reloadRequests({ silent: true }),
|
||||
reloadWorkbenchApprovalRequests()
|
||||
])
|
||||
return payload
|
||||
}
|
||||
|
||||
function resolveWorkbenchRequestKey(request) {
|
||||
return String(request?.claimId || request?.id || request?.claimNo || '').trim()
|
||||
}
|
||||
|
||||
function mergeWorkbenchRequests(primaryRequests = [], approvalRequests = []) {
|
||||
const merged = new Map()
|
||||
for (const request of [...primaryRequests, ...approvalRequests]) {
|
||||
const key = resolveWorkbenchRequestKey(request)
|
||||
if (key) {
|
||||
merged.set(key, request)
|
||||
}
|
||||
}
|
||||
return Array.from(merged.values())
|
||||
}
|
||||
|
||||
function isSameRequestIdentity(request, requestId) {
|
||||
const normalizedId = String(requestId || '').trim()
|
||||
if (!request || !normalizedId) {
|
||||
@@ -185,16 +220,20 @@ export function useAppShell() {
|
||||
return
|
||||
}
|
||||
if (view === 'workbench') {
|
||||
void ensureRequestsLoaded()
|
||||
void reloadWorkbenchRequests()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const workbenchSummary = computed(() =>
|
||||
buildWorkbenchSummary(requests.value, currentUser.value)
|
||||
)
|
||||
|
||||
|
||||
const workbenchRequests = computed(() =>
|
||||
mergeWorkbenchRequests(requests.value, workbenchApprovalRequests.value)
|
||||
)
|
||||
|
||||
const workbenchSummary = computed(() =>
|
||||
buildWorkbenchSummary(workbenchRequests.value, currentUser.value)
|
||||
)
|
||||
|
||||
const topBarView = computed(() => {
|
||||
if (detailMode.value) {
|
||||
const request = selectedRequest.value || {}
|
||||
@@ -207,46 +246,46 @@ export function useAppShell() {
|
||||
: '查看报销明细、票据材料、审批进度与风险提示。'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return currentView.value
|
||||
})
|
||||
|
||||
const requestSummary = computed(() =>
|
||||
filteredRequests.value.reduce(
|
||||
(summary, item) => {
|
||||
const request = normalizeRequestForUi(item)
|
||||
if (!request) {
|
||||
return summary
|
||||
}
|
||||
|
||||
summary.total += 1
|
||||
|
||||
if (request.approvalKey === 'draft') {
|
||||
summary.draft += 1
|
||||
} else if (request.approvalKey === 'in_progress') {
|
||||
summary.inProgress += 1
|
||||
} else if (request.approvalKey === 'supplement') {
|
||||
summary.supplement += 1
|
||||
} else if (request.approvalKey === 'completed') {
|
||||
summary.completed += 1
|
||||
}
|
||||
|
||||
return summary
|
||||
},
|
||||
{ total: 0, draft: 0, inProgress: 0, supplement: 0, completed: 0 }
|
||||
)
|
||||
)
|
||||
|
||||
function handleApprove(request) {
|
||||
const message = approveRequest(request)
|
||||
toast(message)
|
||||
}
|
||||
|
||||
function handleReject(request) {
|
||||
const message = rejectRequest(request)
|
||||
toast(message)
|
||||
}
|
||||
|
||||
|
||||
const requestSummary = computed(() =>
|
||||
filteredRequests.value.reduce(
|
||||
(summary, item) => {
|
||||
const request = normalizeRequestForUi(item)
|
||||
if (!request) {
|
||||
return summary
|
||||
}
|
||||
|
||||
summary.total += 1
|
||||
|
||||
if (request.approvalKey === 'draft') {
|
||||
summary.draft += 1
|
||||
} else if (request.approvalKey === 'in_progress') {
|
||||
summary.inProgress += 1
|
||||
} else if (request.approvalKey === 'supplement') {
|
||||
summary.supplement += 1
|
||||
} else if (request.approvalKey === 'completed') {
|
||||
summary.completed += 1
|
||||
}
|
||||
|
||||
return summary
|
||||
},
|
||||
{ total: 0, draft: 0, inProgress: 0, supplement: 0, completed: 0 }
|
||||
)
|
||||
)
|
||||
|
||||
function handleApprove(request) {
|
||||
const message = approveRequest(request)
|
||||
toast(message)
|
||||
}
|
||||
|
||||
function handleReject(request) {
|
||||
const message = rejectRequest(request)
|
||||
toast(message)
|
||||
}
|
||||
|
||||
function handleNavigate(view) {
|
||||
smartEntryOpen.value = false
|
||||
const shouldRefreshCurrentDocumentCenter =
|
||||
@@ -258,7 +297,7 @@ export function useAppShell() {
|
||||
void reloadDocumentCenterRequests()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function openFinancialAssistantCreate(source) {
|
||||
if (smartEntryOpen.value) {
|
||||
smartEntryRevealToken.value += 1
|
||||
@@ -287,28 +326,28 @@ export function useAppShell() {
|
||||
function openExpenseApplicationCreate() {
|
||||
openFinancialAssistantCreate(SMART_ENTRY_SOURCE_APPLICATION)
|
||||
}
|
||||
|
||||
function resolveCurrentUserId() {
|
||||
const user = currentUser.value || {}
|
||||
return String(user.username || user.name || 'anonymous').trim() || 'anonymous'
|
||||
}
|
||||
|
||||
function resolveSmartEntryClaimScope(payload = {}) {
|
||||
const request = payload.request && typeof payload.request === 'object' ? payload.request : null
|
||||
const payloadScope = payload.scope && typeof payload.scope === 'object' ? payload.scope : null
|
||||
const claimId = String(
|
||||
payloadScope?.claimId ||
|
||||
payloadScope?.claim_id ||
|
||||
request?.claimId ||
|
||||
request?.claim_id ||
|
||||
''
|
||||
).trim()
|
||||
if (!claimId) {
|
||||
return null
|
||||
}
|
||||
return { type: 'claim', claimId }
|
||||
}
|
||||
|
||||
|
||||
function resolveCurrentUserId() {
|
||||
const user = currentUser.value || {}
|
||||
return String(user.username || user.name || 'anonymous').trim() || 'anonymous'
|
||||
}
|
||||
|
||||
function resolveSmartEntryClaimScope(payload = {}) {
|
||||
const request = payload.request && typeof payload.request === 'object' ? payload.request : null
|
||||
const payloadScope = payload.scope && typeof payload.scope === 'object' ? payload.scope : null
|
||||
const claimId = String(
|
||||
payloadScope?.claimId ||
|
||||
payloadScope?.claim_id ||
|
||||
request?.claimId ||
|
||||
request?.claim_id ||
|
||||
''
|
||||
).trim()
|
||||
if (!claimId) {
|
||||
return null
|
||||
}
|
||||
return { type: 'claim', claimId }
|
||||
}
|
||||
|
||||
function isDetailClaimScopedPayload(payload = {}) {
|
||||
return String(payload.source || '').trim() === 'detail' && Boolean(resolveSmartEntryClaimScope(payload))
|
||||
}
|
||||
@@ -451,7 +490,7 @@ export function useAppShell() {
|
||||
const status = String(payload.status || payload.claimStatus || '').trim()
|
||||
const approvalStage = String(payload.approvalStage || payload.approval_stage || '').trim()
|
||||
const isApplicationDocument = isApplicationDocumentPayload(payload, claimNo)
|
||||
await reloadRequests()
|
||||
await reloadWorkbenchRequests()
|
||||
if (status === 'submitted') {
|
||||
if (isApplicationDocument) {
|
||||
toast(`${claimNo || '该'}申请单已提交${approvalStage ? `,当前节点:${approvalStage}` : ',等待直属领导审批'}。`)
|
||||
@@ -505,7 +544,7 @@ export function useAppShell() {
|
||||
}
|
||||
|
||||
async function handleRequestUpdated() {
|
||||
await reloadRequests()
|
||||
await reloadWorkbenchRequests()
|
||||
await refreshSelectedRequestDetail(String(route.params.requestId || ''))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user