feat: 新增预算费控模型与报销审批流引擎

后端新增预算费控服务和报销单审批流模块,引入申请人费用画像
算法,优化知识库 RAG 运行时和同步逻辑,完善报销单工作流常
量和明细同步,更新差旅报销规则电子表格,前端新增预算分析
组件和数字员工模型,完善审批对话框和洞察面板交互,优化侧
边栏和顶栏样式,补充单元测试。
This commit is contained in:
caoxiaozhu
2026-05-27 17:31:27 +08:00
parent cbb98f4469
commit d4d5d40569
75 changed files with 5393 additions and 686 deletions

View File

@@ -9,33 +9,52 @@
</svg>
</div>
<strong class="brand-name">{{ displayCompanyName }}</strong>
<button
class="rail-collapse-btn"
type="button"
:aria-label="collapsed ? '展开侧边栏' : '折叠侧边栏'"
:title="collapsed ? '展开侧边栏' : '折叠侧边栏'"
:aria-expanded="!collapsed"
@click="emit('toggle-collapse')"
<ElTooltip
:content="collapseTooltipContent"
placement="right"
effect="light"
:show-after="180"
:hide-after="0"
:offset="12"
popper-class="rail-tooltip-popper"
>
<i :class="collapsed ? 'mdi mdi-chevron-right' : 'mdi mdi-chevron-left'"></i>
</button>
<button
class="rail-collapse-btn"
type="button"
:aria-label="collapseTooltipContent"
:aria-expanded="!collapsed"
@click="emit('toggle-collapse')"
>
<i :class="collapsed ? 'mdi mdi-chevron-right' : 'mdi mdi-chevron-left'"></i>
</button>
</ElTooltip>
</div>
<nav class="rail-nav" aria-label="功能导航">
<button
<ElTooltip
v-for="item in decoratedNavItems"
:key="item.id"
class="nav-btn"
:class="{ active: activeView === item.id }"
type="button"
:title="collapsed ? item.displayLabel : undefined"
@click="emit('navigate', item.id)"
:content="item.displayLabel"
placement="right"
effect="light"
:disabled="!collapsed"
:show-after="180"
:hide-after="0"
:offset="12"
popper-class="rail-tooltip-popper"
>
<span class="nav-icon" v-html="item.icon"></span>
<span class="nav-label">{{ item.displayLabel }}</span>
<span v-if="item.hasNewMessage" class="nav-unread-dot" aria-hidden="true"></span>
<span v-if="item.badge" class="nav-badge">{{ item.badge }}</span>
</button>
<button
class="nav-btn"
:class="{ active: activeView === item.id }"
type="button"
@click="emit('navigate', item.id)"
>
<span class="nav-icon" v-html="item.icon"></span>
<span class="nav-label">{{ item.displayLabel }}</span>
<span v-if="item.hasNewMessage" class="nav-unread-dot" aria-hidden="true"></span>
<span v-if="item.badge" class="nav-badge">{{ item.badge }}</span>
</button>
</ElTooltip>
</nav>
<div
@@ -69,19 +88,31 @@
</div>
</Teleport>
<div class="user-summary" tabindex="0" aria-label="用户信息" :title="collapsed ? displayUser.name : undefined">
<span class="user-avatar">{{ displayUser.avatar }}</span>
<span class="user-copy">
<strong>{{ displayUser.name }}</strong>
<span>{{ displayUser.role }}</span>
</span>
<i class="mdi mdi-chevron-up"></i>
</div>
<ElTooltip
:content="userTooltipContent"
placement="top"
effect="light"
:disabled="!collapsed"
:show-after="180"
:hide-after="0"
:offset="10"
popper-class="rail-tooltip-popper"
>
<div class="user-summary" tabindex="0" :aria-label="userTooltipContent">
<span class="user-avatar">{{ displayUser.avatar }}</span>
<span class="user-copy">
<strong>{{ displayUser.name }}</strong>
<span>{{ displayUser.role }}</span>
</span>
<i class="mdi mdi-chevron-up"></i>
</div>
</ElTooltip>
</div>
</aside>
</template>
<script setup>
import { ElTooltip } from 'element-plus'
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { useDocumentCenterInbox } from '../../composables/useDocumentCenterInbox.js'
@@ -154,6 +185,8 @@ const displayUser = computed(() => ({
}))
const displayCompanyName = computed(() => props.companyName || 'X-Financial')
const collapseTooltipContent = computed(() => (props.collapsed ? '展开侧边栏' : '折叠侧边栏'))
const userTooltipContent = computed(() => [displayUser.value.name, displayUser.value.role].filter(Boolean).join(' · '))
const userMenuOpen = ref(false)
let userMenuCloseTimer = null

View File

@@ -91,7 +91,7 @@
class="detail-alert-pill"
:class="alert.tone"
>
<i class="mdi mdi-alert-circle-outline"></i>
<i :class="alert.icon || 'mdi mdi-alert-circle-outline'"></i>
<span>{{ alert.label }}</span>
</span>
</div>