feat: 新增风险图谱算法与系统仪表盘及操作反馈体系

后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL
校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计,
优化 agent 运行和编排执行链路,清理旧开发文档,前端新增
系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈
对话框和工作台日期选择器,优化报销创建和审批详情交互,
补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-30 15:46:51 +08:00
parent 4c59941ec6
commit 7989f3a159
314 changed files with 30073 additions and 20626 deletions

View File

@@ -61,28 +61,41 @@
</section>
<section class="profile-panel profile-radar-panel" aria-label="行为雷达图">
<div class="profile-section-title">
<div class="profile-section-title profile-radar-title">
<div>
<span>行为雷达</span>
<small>分数越高行为特征越明显</small>
<small>{{ currentRadarView.description }}</small>
</div>
<ElSelect
v-model="selectedRadarView"
class="profile-radar-view-select"
size="small"
aria-label="切换行为雷达视角"
>
<ElOption
v-for="option in radarViewOptions"
:key="option.value"
:label="option.shortLabel"
:value="option.value"
/>
</ElSelect>
</div>
<div v-if="radarDimensions.length" class="profile-radar-layout">
<div v-if="filteredRadarDimensions.length" class="profile-radar-layout">
<RadarChart
:key="radarRenderKey"
class="profile-radar-chart"
:items="radarDimensions"
label="用户画像评分"
:items="filteredRadarDimensions"
:label="`${currentRadarView.shortLabel}评分`"
/>
</div>
<p v-else class="profile-panel-empty profile-radar-empty">暂无可展示的雷达维度</p>
<div v-if="tags.length" class="profile-behavior-tags" aria-label="行为标签">
<div :class="['profile-behavior-tags', { 'is-empty': !filteredBehaviorTags.length }]" :aria-hidden="!filteredBehaviorTags.length" aria-label="行为标签">
<span class="profile-behavior-tags-title">行为标签</span>
<div class="profile-behavior-tag-list">
<div v-if="filteredBehaviorTags.length" class="profile-behavior-tag-list">
<span
v-for="tag in tags"
v-for="tag in filteredBehaviorTags"
:key="`behavior-${tag.code}`"
:class="[
'profile-behavior-tag',
@@ -137,10 +150,12 @@
import { computed, nextTick, ref, watch } from 'vue'
import { ElButton } from 'element-plus/es/components/button/index.mjs'
import { ElDialog } from 'element-plus/es/components/dialog/index.mjs'
import { ElOption, ElSelect } from 'element-plus/es/components/select/index.mjs'
import { ElTag } from 'element-plus/es/components/tag/index.mjs'
import ExpenseProfileTagPager from './ExpenseProfileTagPager.vue'
import RadarChart from '../charts/RadarChart.vue'
import { USER_PROFILE_RADAR_VIEW_OPTIONS, filterUserProfileRadarDimensions, filterUserProfileTagsByRadarView } from '../../utils/employeeProfileViewModel.js'
const props = defineProps({
visible: { type: Boolean, default: false },
@@ -148,6 +163,7 @@ const props = defineProps({
metrics: { type: Array, default: () => [] },
tags: { type: Array, default: () => [] },
radarDimensions: { type: Array, default: () => [] },
radarDefaultView: { type: String, default: 'financial_risk' },
operations: { type: Array, default: () => [] },
loading: { type: Boolean, default: false },
errorMessage: { type: String, default: '' },
@@ -156,6 +172,11 @@ const props = defineProps({
const emit = defineEmits(['close'])
const radarRenderKey = ref(0)
const selectedRadarView = ref(props.radarDefaultView)
const radarViewOptions = USER_PROFILE_RADAR_VIEW_OPTIONS
const currentRadarView = computed(() => radarViewOptions.find((option) => option.value === selectedRadarView.value) || radarViewOptions[0])
const filteredRadarDimensions = computed(() => filterUserProfileRadarDimensions(props.radarDimensions, selectedRadarView.value))
const filteredBehaviorTags = computed(() => filterUserProfileTagsByRadarView(props.tags, selectedRadarView.value))
function emitClose() {
emit('close')
@@ -225,6 +246,27 @@ watch(
})
}
)
watch(
() => props.radarDefaultView,
(value) => {
selectedRadarView.value = value || 'financial_risk'
},
{ immediate: true }
)
watch(
[filteredRadarDimensions, selectedRadarView],
async () => {
if (!props.visible) {
return
}
await nextTick()
scheduleRadarFrame(() => {
radarRenderKey.value += 1
})
}
)
</script>
<style scoped>
@@ -469,6 +511,21 @@ watch(
font-weight: 850;
}
.profile-radar-title { align-items: flex-start; }
.profile-radar-view-select {
width: 118px;
flex: 0 0 118px;
}
.profile-radar-view-select :deep(.el-select__wrapper) {
min-height: 28px;
border-radius: 4px;
box-shadow: 0 0 0 1px #cbd5e1 inset;
color: #334155;
font-size: 12px;
font-weight: 750;
}
.profile-operation-list {
display: grid;
gap: 8px;
@@ -536,9 +593,12 @@ watch(
display: grid;
gap: 8px;
padding-top: 10px;
min-height: 59px;
border-top: 1px solid #e8eef5;
}
.profile-behavior-tags.is-empty { visibility: hidden; }
.profile-behavior-tags-title {
color: #0f172a;
font-size: 12px;