feat: 优化差旅报销预审流程与个人工作台 UI 体系

- 完善 user_agent_application 申请差旅报销预审槽位与消息组装
- 增强预算助理报告与风险建议卡片交互
- 重构登录页视觉样式与移动端响应式适配
- 优化个人工作台、文档中心、政策中心、员工管理等页面布局
- 拆分 travelRequestDetailPreReviewModel 为 advice/submit 模型
- 补充报销草稿、风险复核、Item Sync 与模板执行器测试覆盖
This commit is contained in:
caoxiaozhu
2026-06-02 14:01:51 +08:00
parent 92444e7eae
commit ca691f3ee0
107 changed files with 5663 additions and 1542 deletions

View File

@@ -7,7 +7,7 @@
note="把费用申请、报销进度、制度问答和待办处理集中到一个入口。"
/>
<article class="panel assistant-hero" :style="{ '--assistant-bg-image': `url(${homepageBackground})` }">
<article class="panel assistant-hero" :style="{ '--assistant-bg-image': `url(${workbenchHeroBackground})` }">
<div class="assistant-copy">
<h1>{{ displayUserName }}我是您的 <span>AI 费用助手</span></h1>
@@ -358,16 +358,17 @@ import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import PanelHead from '../shared/PanelHead.vue'
import ExpenseProfileDetailModal from './ExpenseProfileDetailModal.vue'
import WorkbenchListIcon from '../shared/WorkbenchListIcon.vue'
import homepageBackground from '../../assets/homepage_backgraound.png'
import workbenchHeroBackground from '../../assets/personal-workbench-hero-bg-theme-base.webp'
import { useSystemState } from '../../composables/useSystemState.js'
import { useToast } from '../../composables/useToast.js'
import { useWorkbenchComposerDate } from '../../composables/useWorkbenchComposerDate.js'
import {
assistantCapabilities,
buildExpenseStatItems,
filterAssistantCapabilitiesForUser,
progressItems,
progressSteps,
quickPromptItems,
resolveWorkbenchCapabilityGridClass,
todoItems,
} from '../../data/personalWorkbench.js'
import { fetchAgentRuns } from '../../services/agentAssets.js'
@@ -433,9 +434,6 @@ let employeeProfileLoadSeq = 0
const MAX_ATTACHMENTS = 10
const SESSION_TYPE_EXPENSE = 'expense'
const SESSION_TYPE_KNOWLEDGE = 'knowledge'
const FINANCIAL_CAPABILITY_KEYS = new Set(['budget-planning', 'finance-analysis'])
const FINANCIAL_CAPABILITY_ROLE_CODES = new Set(['budget_monitor', 'executive', 'admin'])
const FINANCIAL_CAPABILITY_ROLE_LABELS = new Set(['预算监控员', '高级财务人员', '管理员'])
const hasExpenseConversation = computed(() =>
Boolean(latestExpenseConversation.value?.conversation_id || latestExpenseConversation.value?.conversationId)
@@ -456,28 +454,8 @@ const composerPendingLabel = computed(() => {
}
return ''
})
const currentRoleCodes = computed(() => {
const user = currentUser.value || {}
const rawCodes = Array.isArray(user.roleCodes)
? user.roleCodes
: Array.isArray(user.role_codes)
? user.role_codes
: []
return new Set(rawCodes.map((code) => String(code || '').trim().toLowerCase()).filter(Boolean))
})
const canViewFinancialCapabilities = computed(() => {
const user = currentUser.value || {}
const roleLabel = String(user.role || '').trim()
return Boolean(user.isAdmin)
|| FINANCIAL_CAPABILITY_ROLE_LABELS.has(roleLabel)
|| Array.from(currentRoleCodes.value).some((code) => FINANCIAL_CAPABILITY_ROLE_CODES.has(code))
})
const visibleAssistantCapabilities = computed(() =>
assistantCapabilities.filter((item) => canViewFinancialCapabilities.value || !FINANCIAL_CAPABILITY_KEYS.has(item.key))
)
const capabilityGridClass = computed(() =>
canViewFinancialCapabilities.value ? 'capability-grid--privileged' : 'capability-grid--standard'
)
const visibleAssistantCapabilities = computed(() => filterAssistantCapabilitiesForUser(currentUser.value))
const capabilityGridClass = computed(() => resolveWorkbenchCapabilityGridClass(currentUser.value))
const expenseStatItems = computed(() => buildExpenseStatItems(props.workbenchSummary))
const visibleExpenseStatItems = computed(() => {
const preferredKeys = ['monthly-amount', 'monthly-count', 'in-review', 'pending-payment']
@@ -817,6 +795,7 @@ watch(currentUserProfileKey, (nextKey, previousKey) => {
</script>
<style scoped src="../../assets/styles/components/personal-workbench.css"></style>
<style scoped src="../../assets/styles/components/personal-workbench-glass.css"></style>
<style scoped src="../../assets/styles/components/personal-workbench-composer-date.css"></style>
<style scoped src="../../assets/styles/components/personal-workbench-insights.css"></style>
<style scoped src="../../assets/styles/components/personal-workbench-responsive.css"></style>