feat(ai): add tenant-safe hierarchical expense learning
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user