feat(ai): add tenant-safe hierarchical expense learning

This commit is contained in:
caoxiaozhu
2026-07-16 14:30:41 +08:00
parent 6bdf65bc24
commit ee88a36baf
65 changed files with 6909 additions and 232 deletions

View File

@@ -1,17 +1,20 @@
<template>
<div v-if="appliedMemories.length || learningReceipts.length" class="application-memory-stack">
<div
v-if="appliedMemories.length || memoryNotices.length || learningReceipts.length"
class="application-memory-stack"
>
<section
v-if="appliedMemories.length"
class="application-memory-panel"
aria-label="本次申请使用的历史偏好"
aria-label="本次申请使用的智能记忆"
>
<header class="application-memory-panel-head">
<span class="application-memory-panel-icon" aria-hidden="true">
<i class="mdi mdi-history"></i>
</span>
<span>
<strong>个性化填充</strong>
<small>以下内容来自已确认的历史偏好您仍可直接修改上方字段</small>
<strong>智能记忆填充</strong>
<small>企业制度部门基线和个人偏好会按优先级应用您仍可直接修改上方字段</small>
</span>
</header>
<div class="application-memory-list">
@@ -23,13 +26,17 @@
<div class="application-memory-copy">
<span class="application-memory-status">
<i class="mdi mdi-check-circle-outline" aria-hidden="true"></i>
已按历史偏好填入
{{ resolveMemorySourceTitle(memory) }}
</span>
<strong>{{ memory.fieldLabel }}{{ memory.value }}</strong>
<small v-if="memory.message">{{ memory.message }}</small>
<em>{{ ui.resolveApplicationMemoryEvidenceText(memory) }}</em>
<small v-if="memory.conflicts.length" class="application-memory-conflict-note">
已按适用优先级选择当前设置其他层级设置未自动采用
</small>
<em>{{ resolveMemorySupportingText(memory) }}</em>
</div>
<button
v-if="canForgetMemory(memory)"
type="button"
class="application-memory-forget-btn"
:disabled="ui.isForgettingApplicationMemory(memory)"
@@ -46,6 +53,27 @@
</div>
</section>
<section
v-if="memoryNotices.length"
class="application-memory-notice-panel"
role="status"
aria-live="polite"
aria-label="本次未自动应用的记忆说明"
>
<header>
<i class="mdi mdi-information-outline" aria-hidden="true"></i>
<strong>需要您确认的设置</strong>
</header>
<article
v-for="memory in memoryNotices"
:key="`${message.id}-memory-notice-${memory.memoryId}`"
class="application-memory-notice"
>
<strong>{{ memory.fieldLabel }}</strong>
<span>{{ resolveMemoryNoticeText(memory) }}</span>
</article>
</section>
<section
v-if="learningReceipts.length"
class="application-learning-receipt-panel"
@@ -75,6 +103,16 @@
<script setup>
import { computed } from 'vue'
import { resolveAiApplicationPreviewMemories } from '../../services/aiApplicationPreviewActions.js'
const MEMORY_NOTICE_STATUSES = new Set([
'conflict',
'conflicted',
'not_applied',
'overridden',
'skipped',
'suppressed'
])
const props = defineProps({
message: {
@@ -88,7 +126,44 @@ const props = defineProps({
})
const appliedMemories = computed(() => props.ui.resolveAppliedApplicationPreviewMemories(props.message))
const normalizedMemories = computed(() => (
resolveAiApplicationPreviewMemories(props.message?.applicationPreview || {})
))
const memoryNotices = computed(() => normalizedMemories.value.filter((memory) => (
memory.status !== 'applied'
&& (memory.conflicts.length > 0 || MEMORY_NOTICE_STATUSES.has(memory.status))
)))
const learningReceipts = computed(() => props.ui.resolveVisibleApplicationLearningReceipts(props.message))
function resolveMemorySourceTitle(memory = {}) {
if (memory.scopeType === 'enterprise') return '企业制度填充'
if (memory.scopeType === 'department') return '部门基线填充'
return '个性化填充'
}
function resolveMemorySupportingText(memory = {}) {
if (memory.scopeType === 'enterprise') {
return '由企业管理员维护,仅影响自动填充。'
}
if (memory.scopeType === 'department') {
return '由当前部门的适用设置提供,仅影响自动填充。'
}
return props.ui.resolveApplicationMemoryEvidenceText(memory)
}
function canForgetMemory(memory = {}) {
return memory.scopeType === 'user' && memory.canRevoke === true
}
function resolveMemoryNoticeText(memory = {}) {
if (memory.conflicts.length > 0 || ['conflict', 'conflicted'].includes(memory.status)) {
return '检测到同层级设置冲突,本次未自动填入,请按实际情况确认。'
}
if (['overridden', 'suppressed'].includes(memory.status)) {
return '存在更高优先级的适用设置,本次未使用该项。'
}
return '该项本次未自动填入,请按实际情况确认。'
}
</script>
<style scoped src="../../assets/styles/components/travel-reimbursement-message-memory.css"></style>