feat: 数字员工财务报告体系与定时提醒及看板快照调度
- 新增数字员工财务报告生成、邮件投递与渲染调度器 - 引入员工画像扫描调度与定时提醒任务 - 完善财务看板快照、排行口径与部门人员占比计算 - 优化数字员工工作看板仪表盘与技能目录 - 增强前端总览页图表、工作台摘要与顶部导航栏交互 - 新增差旅申请规划推动提醒与报销创建会话状态管理 - 补充财务报告、看板调度、数字员工工作记录测试覆盖
This commit is contained in:
@@ -2,9 +2,13 @@ import { ref } from 'vue'
|
||||
|
||||
import {
|
||||
APPLICATION_TRANSPORT_MODE_OPTIONS,
|
||||
applyApplicationPolicyEstimateError,
|
||||
applyApplicationPolicyEstimateResult,
|
||||
buildApplicationPreviewRows,
|
||||
buildApplicationPolicyEstimateRequest,
|
||||
buildLocalApplicationPreviewMessage,
|
||||
normalizeApplicationPreview,
|
||||
resolveApplicationDaysFromDateRange,
|
||||
refreshApplicationPreviewTransportEstimate
|
||||
} from '../../utils/expenseApplicationPreview.js'
|
||||
import { waitForMockApplicationTransportQuote } from '../../utils/expenseApplicationEstimate.js'
|
||||
@@ -44,6 +48,27 @@ function shouldRefreshTransportEstimate(fieldKey) {
|
||||
return ['transportMode', 'time', 'location', 'days'].includes(fieldKey)
|
||||
}
|
||||
|
||||
function resolveEditorCurrentUser(currentUser) {
|
||||
if (currentUser && typeof currentUser === 'object' && 'value' in currentUser) {
|
||||
return currentUser.value || {}
|
||||
}
|
||||
return currentUser || {}
|
||||
}
|
||||
|
||||
function buildEditedApplicationPreviewFields(fields = {}, editor = {}, nextValue = '') {
|
||||
const nextFields = {
|
||||
...fields,
|
||||
[editor.fieldKey]: nextValue
|
||||
}
|
||||
if (editor.fieldKey === 'time') {
|
||||
const resolvedDays = resolveApplicationDaysFromDateRange(nextValue)
|
||||
if (resolvedDays) {
|
||||
nextFields.days = resolvedDays
|
||||
}
|
||||
}
|
||||
return nextFields
|
||||
}
|
||||
|
||||
function buildTransportEstimatePendingPreview(preview = {}) {
|
||||
const fields = preview?.fields || {}
|
||||
return normalizeApplicationPreview({
|
||||
@@ -57,9 +82,29 @@ function buildTransportEstimatePendingPreview(preview = {}) {
|
||||
})
|
||||
}
|
||||
|
||||
export function useApplicationPreviewEditor({ persistSessionState, toast } = {}) {
|
||||
export function useApplicationPreviewEditor({
|
||||
persistSessionState,
|
||||
toast,
|
||||
calculateTravelReimbursement,
|
||||
currentUser
|
||||
} = {}) {
|
||||
const applicationPreviewEditor = ref(buildEmptyEditor())
|
||||
|
||||
async function refreshApplicationPreviewEstimate(preview = {}) {
|
||||
const user = resolveEditorCurrentUser(currentUser)
|
||||
const estimateRequest = buildApplicationPolicyEstimateRequest(preview, user)
|
||||
if (estimateRequest.canCalculate && typeof calculateTravelReimbursement === 'function') {
|
||||
try {
|
||||
const result = await calculateTravelReimbursement(estimateRequest.payload)
|
||||
return applyApplicationPolicyEstimateResult(preview, result, user)
|
||||
} catch (error) {
|
||||
console.warn('Application preview estimate refresh failed:', error)
|
||||
return applyApplicationPolicyEstimateError(preview, error, user)
|
||||
}
|
||||
}
|
||||
return refreshApplicationPreviewTransportEstimate(preview)
|
||||
}
|
||||
|
||||
function resolveApplicationPreviewRows(message) {
|
||||
return buildApplicationPreviewRows(message?.applicationPreview || {})
|
||||
}
|
||||
@@ -158,25 +203,29 @@ export function useApplicationPreviewEditor({ persistSessionState, toast } = {})
|
||||
}
|
||||
const nextPreview = normalizeApplicationPreview({
|
||||
...message.applicationPreview,
|
||||
fields: {
|
||||
...(message.applicationPreview.fields || {}),
|
||||
[editor.fieldKey]: nextValue
|
||||
}
|
||||
fields: buildEditedApplicationPreviewFields(
|
||||
message.applicationPreview.fields || {},
|
||||
editor,
|
||||
nextValue
|
||||
)
|
||||
})
|
||||
const needRefreshTransport = shouldRefreshTransportEstimate(editor.fieldKey) && String(nextPreview.fields?.transportMode || '').trim()
|
||||
message.applicationPreview = needRefreshTransport
|
||||
const needRefreshEstimate = shouldRefreshTransportEstimate(editor.fieldKey)
|
||||
const transportMode = String(nextPreview.fields?.transportMode || '').trim()
|
||||
message.applicationPreview = needRefreshEstimate
|
||||
? 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)
|
||||
if (needRefreshEstimate) {
|
||||
if (transportMode) {
|
||||
await waitForMockApplicationTransportQuote({
|
||||
transportMode,
|
||||
location: nextPreview.fields.matchedCity || nextPreview.fields.location,
|
||||
time: nextPreview.fields.time
|
||||
})
|
||||
}
|
||||
const refreshedPreview = await refreshApplicationPreviewEstimate(nextPreview)
|
||||
message.applicationPreview = refreshedPreview
|
||||
message.text = buildLocalApplicationPreviewMessage(refreshedPreview)
|
||||
persistSessionState?.()
|
||||
|
||||
Reference in New Issue
Block a user