feat: 增强风险规则生成引擎与预算中心页面

后端拆分风险规则生成为解释器、语义分析、本体对齐等子模块,
优化模板执行和流程图生成,完善员工种子数据和导入逻辑,增强
报销单权限策略和草稿持久化,前端新增预算中心视图和趋势图
组件,重构审计页面和风险规则测试对话框交互,完善文档中心
和报销创建页面细节,补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-26 09:15:14 +08:00
parent d0e946cf47
commit 0e861d8fa6
150 changed files with 14953 additions and 4099 deletions

View File

@@ -1,8 +1,7 @@
import { computed, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useApprovalInbox } from './useApprovalInbox.js'
import { useNavigation, navItems } from './useNavigation.js'
import { useNavigation, navItems } from './useNavigation.js'
import { useRequests } from './useRequests.js'
import { useSystemState } from './useSystemState.js'
import { useToast } from './useToast.js'
@@ -23,14 +22,15 @@ export function useAppShell() {
const smartEntryOpen = ref(false)
const smartEntryContext = ref({
prompt: '',
source: 'requests',
source: 'documents',
request: null,
files: [],
conversation: null,
scope: null
})
const smartEntrySessionId = ref(0)
const smartEntryInvalidatedDraftClaimId = ref('')
const smartEntrySessionId = ref(0)
const smartEntryRevealToken = ref(0)
const smartEntryInvalidatedDraftClaimId = ref('')
const selectedRequestSnapshot = ref(null)
const { activeView, currentView, setView } = useNavigation()
@@ -49,7 +49,6 @@ export function useAppShell() {
} = useRequests()
const { currentUser } = useSystemState()
const { toast } = useToast()
const { refreshApprovalInbox } = useApprovalInbox()
const customRange = ref({ start: '2024-07-06', end: '2024-07-12' })
@@ -83,21 +82,14 @@ export function useAppShell() {
return null
})
const detailMode = computed(() => ['app-request-detail', 'app-document-detail'].includes(route.name))
const logDetailMode = computed(() => route.name === 'app-log-detail')
const detailAlerts = computed(() => (detailMode.value ? buildDetailAlerts(selectedRequest.value) : []))
const requestsListActive = computed(() => activeView.value === 'requests' && !detailMode.value)
const documentsListActive = computed(() => activeView.value === 'documents' && !detailMode.value)
const workbenchActive = computed(() => activeView.value === 'workbench')
watch(requestsListActive, (isActive, wasActive) => {
if (isActive && !wasActive) {
void reloadRequests()
}
})
watch(documentsListActive, (isActive, wasActive) => {
const detailMode = computed(() => route.name === 'app-document-detail')
const logDetailMode = computed(() => route.name === 'app-log-detail')
const detailAlerts = computed(() => (detailMode.value ? buildDetailAlerts(selectedRequest.value) : []))
const documentsListActive = computed(() => activeView.value === 'documents' && !detailMode.value)
const workbenchActive = computed(() => activeView.value === 'workbench')
watch(documentsListActive, (isActive, wasActive) => {
if (isActive && !wasActive) {
void reloadRequests()
}
@@ -178,6 +170,10 @@ export function useAppShell() {
}
function openFinancialAssistantCreate(source) {
if (smartEntryOpen.value) {
smartEntryRevealToken.value += 1
return
}
smartEntryOpen.value = true
smartEntryContext.value = {
prompt: '',
@@ -237,6 +233,7 @@ export function useAppShell() {
return (
documentType === 'application'
|| documentType === 'expense_application'
|| normalizedClaimNo.startsWith('AP-')
|| normalizedClaimNo.startsWith('APP-')
)
}
@@ -266,8 +263,12 @@ export function useAppShell() {
}
}
async function openSmartEntry(payload = {}) {
const conversation = await resolveSmartEntryConversation(payload)
async function openSmartEntry(payload = {}) {
if (smartEntryOpen.value) {
smartEntryRevealToken.value += 1
return
}
const conversation = await resolveSmartEntryConversation(payload)
const scope = resolveSmartEntryClaimScope(payload)
smartEntryOpen.value = true
@@ -294,13 +295,12 @@ export function useAppShell() {
await reloadRequests()
if (status === 'submitted') {
smartEntryOpen.value = false
void refreshApprovalInbox()
toast(
isApplicationDocument
? `${claimNo || '该'}申请单已提交${approvalStage ? `,当前节点:${approvalStage}` : ',等待直属领导审批'}`
: `${claimNo || '该'}单据已完成 AI预审${approvalStage ? `,当前节点:${approvalStage}` : ',并已提交审批'}`
)
router.push({ name: activeView.value === 'documents' ? 'app-documents' : 'app-requests' })
router.push({ name: 'app-documents' })
return
}
toast(
@@ -310,23 +310,21 @@ export function useAppShell() {
)
}
function openRequestDetail(request) {
selectedRequestSnapshot.value = request || null
const routeName = activeView.value === 'documents' ? 'app-document-detail' : 'app-request-detail'
router.push({
name: routeName,
params: { requestId: request.claimId || request.id }
})
}
function closeRequestDetail() {
router.push({ name: activeView.value === 'documents' ? 'app-documents' : 'app-requests' })
}
async function handleRequestUpdated() {
await reloadRequests()
void refreshApprovalInbox()
}
function openRequestDetail(request) {
selectedRequestSnapshot.value = request || null
router.push({
name: 'app-document-detail',
params: { requestId: request.claimId || request.id }
})
}
function closeRequestDetail() {
router.push({ name: 'app-documents' })
}
async function handleRequestUpdated() {
await reloadRequests()
}
async function handleRequestDeleted(payload = {}) {
const deletedClaimId = String(payload.claimId || payload.claim_id || '').trim()
@@ -335,11 +333,10 @@ export function useAppShell() {
smartEntryInvalidatedDraftClaimId.value = deletedClaimId
}
await reloadRequests()
void refreshApprovalInbox()
selectedRequestSnapshot.value = null
router.push({ name: activeView.value === 'documents' ? 'app-documents' : 'app-requests' })
}
await reloadRequests()
selectedRequestSnapshot.value = null
router.push({ name: 'app-documents' })
}
return {
activeRange,
@@ -374,9 +371,10 @@ export function useAppShell() {
selectedRequest,
setView,
smartEntryContext,
smartEntryInvalidatedDraftClaimId,
smartEntryOpen,
smartEntrySessionId,
smartEntryInvalidatedDraftClaimId,
smartEntryOpen,
smartEntryRevealToken,
smartEntrySessionId,
detailAlerts,
toast,
topBarView