feat(web): AI 工作台文件预览/附件关联任务与草稿分支

- 新增 WorkbenchAiFilePreviewDialog 附件预览对话框及 useWorkbenchAiFilePreview,附件支持点击预览
- 新增 attachmentAssociationJobs/linkedReimbursementDraftJobs 前端服务与对应 composable,接入后台任务轮询与状态展示
- 新增 travelReimbursementDraftBranchModel 草稿分支模型,报销关联门控支持跳过/选择草稿
- PersonalWorkbenchAiMode 及各 composable(expense/document/steward/application-preview/attachment-association)重构适配,WorkbenchAiComposer/FileStrip 样式与交互完善
- DocumentsCenter/ReceiptFolder/TravelReimbursementCreate 等视图及 scripts 重构,风险/差旅规划/审批等工具适配
- 新增/更新前端测试:application-result-card、reimbursement-list-preview-fetch、guided-flow、composer-components 等
This commit is contained in:
caoxiaozhu
2026-06-24 10:42:50 +08:00
parent 0264a4b5b4
commit ee730aa31c
73 changed files with 2528 additions and 379 deletions

View File

@@ -155,13 +155,13 @@ export function buildStewardPlanMessageText(plan) {
`${index + 1}. **${buildTaskOrderVerb(index)}${buildTaskOrderTarget(task)}**\n - ${buildTaskOrderActionDescription(task)}`
)
return [
'### 我先帮把步骤理清楚',
'### 我先帮把步骤理清楚',
'',
buildStewardPlanFriendlyIntro(normalized),
'',
...taskLines,
'',
'看这个顺序是否合适?如果没问题,回复 **确定** 就行。我会先帮你进入第一步,需要补充的信息会在具体步骤里再温和提醒。'
'看这个顺序是否合适?如果没问题,回复 **确定** 即可。我会先带您进入第一步,需要补充的信息会在具体步骤里再温和提醒。'
].filter((line, index, lines) => line || lines[index - 1]).join('\n')
}
@@ -457,9 +457,9 @@ function buildPendingFlowConfirmationMessageText(normalized) {
'',
knownTable
? ['我识别到这是一项财务事项,已提取到:', '', knownTable].join('\n')
: '我识别到这是一项财务事项,但还需要确认要进入哪个流程。',
: '我识别到这是一项财务事项,但还需要确认要进入哪个流程。',
'',
normalized.pendingFlowConfirmation.reason || normalized.summary || '当前还不能确定要补办申请还是发起报销。',
normalized.pendingFlowConfirmation.reason || normalized.summary || '当前还不能确定您是要补办申请还是发起报销。',
'',
...candidateLines,
'',
@@ -471,14 +471,14 @@ function buildPendingFlowConfirmationMessageText(normalized) {
function buildGenericReimbursementIntentMessageText() {
return [
'### 我来带发起报销',
'### 我来带发起报销',
'',
'现在只说了要报销,还没告诉我具体是哪类费用。先不用一次性补全所有信息,我会按报销流程一步步带填。',
'现在只说了要报销,还没告诉我具体是哪类费用。先不用一次性补全所有信息,我会按报销流程一步步带填。',
'',
'1. **先选报销场景**',
' - 例如差旅费、交通费、住宿费、业务招待费或办公用品费,不同场景需要的材料不一样。',
'2. **再补关键材料**',
' - 我会继续追问事由、发生时间、金额和票据附件;如果是差旅或招待,还会先帮核对是否需要关联事前申请。',
' - 我会继续追问事由、发生时间、金额和票据附件;如果是差旅或招待,还会先帮核对是否需要关联事前申请。',
'',
'点击下面的 **确定,选择报销场景**,我会进入报销助手继续引导。'
].join('\n')
@@ -602,22 +602,26 @@ function buildTaskOrderTarget(task) {
function buildTaskOrderActionDescription(task) {
const agent = task.assignedAgentLabel || '对应助手'
if (task.taskType === 'expense_application') {
return `我会请${agent}先把申请单草稿整理出来,方便你核对关键信息,再决定是否继续。`
// 申请类:先给行动,再说目的,主语后置
return `这步交给${agent}——先把申请单草稿拉出来给您过目,没问题了再往下走。`
}
if (task.taskType === 'reimbursement') {
if (isGenericReimbursementTask(task)) {
return `我会请${agent}先带你选择报销场景,再逐步补齐事由、时间、金额和票据。`
// 通用报销:换个句式,省掉主语,突出"先定方向"
return `报销还差一个关键信息:具体是哪类费用。${agent}会先带您把报销场景定下来,再逐项补事由、时间、金额和票据。`
}
return `我会请${agent}把票据、金额和制度口径先核清楚,前一步确认后再继续往下走。`
// 有明确场景的报销:直接说动作,不绕弯
return `票据、金额和制度口径,${agent}会一并核清楚;前一步确认后才会继续,不会越级往下推。`
}
return `我会请${agent}先整理可核对的结果,真正执行前仍会让你确认。`
// 兜底:用"等您点头"的语气,区别于上面三条
return `${agent}先把能核对的结果摆出来,真正动手前仍会等您点头。`
}
function buildStewardPlanFriendlyIntro(normalized) {
const taskCountText = normalized.tasks.length > 1
? `${normalized.tasks.length} 个相关事项`
: '1 个事项'
return `我先看了一下,这次主要是 **${taskCountText}**。为了不让步骤混在一起,我会先把要做的事拆开,让每一步都能看清楚、确认后再继续。`
return `我先看了一下,这次主要是 **${taskCountText}**。为了不让步骤混在一起,我会先把要做的事拆开,让每一步都能看清楚、确认后再继续。`
}
function buildTaskOrderDescription(normalized) {
@@ -627,12 +631,12 @@ function buildTaskOrderDescription(normalized) {
return '处理顺序是:先创建申请单,再引导填写报销单。'
}
if (hasApplication) {
return '我会先引导创建申请单并等待确认。'
return '我会先引导创建申请单并等待确认。'
}
if (hasReimbursement) {
return '我会引导填写报销单并等待确认。'
return '我会引导填写报销单并等待确认。'
}
return '我会按识别顺序逐项推进,并在执行前等待确认。'
return '我会按识别顺序逐项推进,并在执行前等待确认。'
}
function buildNextTaskLead(task) {