feat: 添加 AI 分类功能,支持交易自动分类与标签生成

This commit is contained in:
2026-03-12 10:59:44 +08:00
parent 8cbe01dd9c
commit 6a00875246
12 changed files with 594 additions and 27 deletions

View File

@@ -4,6 +4,7 @@ import NotificationBridge, { isNativeNotificationBridgeAvailable } from '../lib/
import { useSettingsStore } from '../stores/settings'
import EchoInput from '../components/EchoInput.vue'
import { BUDGET_CATEGORY_OPTIONS } from '../config/transactionCategories.js'
import { AI_MODEL_OPTIONS, AI_PROVIDER_OPTIONS } from '../config/transactionTags.js'
// 从 Vite 注入的版本号(来源于 package.json用于在设置页展示
// eslint-disable-next-line no-undef
@@ -23,6 +24,24 @@ const aiAutoCategoryEnabled = computed({
set: (value) => settingsStore.setAiAutoCategoryEnabled(value),
})
const aiApiKey = computed({
get: () => settingsStore.aiApiKey,
set: (value) => settingsStore.setAiApiKey(value),
})
const aiAutoApplyThreshold = computed({
get: () => String(settingsStore.aiAutoApplyThreshold ?? 0.9),
set: (value) => settingsStore.setAiAutoApplyThreshold(value),
})
const currentAiProviderLabel = computed(
() => AI_PROVIDER_OPTIONS.find((item) => item.value === settingsStore.aiProvider)?.label || 'DeepSeek',
)
const currentAiModelLabel = computed(
() => AI_MODEL_OPTIONS.find((item) => item.value === settingsStore.aiModel)?.label || 'deepseek-chat',
)
const nativeBridgeReady = isNativeNotificationBridgeAvailable()
const notificationPermissionGranted = ref(true)
const checkingPermission = ref(false)
@@ -373,7 +392,7 @@ onMounted(() => {
</div>
</div>
<!-- AI 自动分类开关预留 -->
<!-- AI 自动分类与标签 -->
<div class="flex flex-col gap-1 p-4">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
@@ -383,9 +402,9 @@ onMounted(() => {
<i class="ph-fill ph-magic-wand" />
</div>
<div>
<p class="font-bold text-stone-700 text-sm">AI 自动分类与标签预留</p>
<p class="font-bold text-stone-700 text-sm">AI 自动分类与标签</p>
<p class="text-[11px] text-stone-400 mt-0.5">
开启后未来会自动补全分类标签生成可复用的商户画像
使用本地 DeepSeek API Key 为新交易补全分类标签记录商户画像
</p>
</div>
</div>
@@ -397,9 +416,42 @@ onMounted(() => {
<div class="w-4 h-4 bg-white rounded-full shadow-sm" />
</button>
</div>
<p v-if="aiAutoCategoryEnabled" class="px-4 pb-1 text-[11px] text-purple-600">
当前版本仅记录偏好后续接入 AI 能力后会自动生效
</p>
<div v-if="aiAutoCategoryEnabled" class="px-4 pt-3 pb-1 space-y-3">
<div class="flex flex-wrap gap-2">
<span class="px-3 py-1 rounded-full bg-stone-100 text-[11px] font-bold text-stone-600">
Provider · {{ currentAiProviderLabel }}
</span>
<span class="px-3 py-1 rounded-full bg-stone-100 text-[11px] font-bold text-stone-600">
Model · {{ currentAiModelLabel }}
</span>
</div>
<EchoInput
v-model="aiApiKey"
label="DeepSeek API Key"
type="password"
placeholder="sk-..."
hint="仅保存在当前设备,本阶段用于本地直连 DeepSeek。"
/>
<EchoInput
v-model="aiAutoApplyThreshold"
label="自动应用阈值0-1"
type="number"
inputmode="decimal"
step="0.05"
placeholder="例如 0.9"
hint="高于该阈值的 AI 分类会自动写入交易分类,低于阈值仅作为建议保留。"
/>
<p
v-if="!settingsStore.aiApiKey"
class="text-[11px] text-amber-600 bg-amber-50 border border-amber-200 rounded-2xl px-3 py-2"
>
当前已开启 AI但还没有填写 DeepSeek API Key保存交易时不会发起 AI 分类请求
</p>
</div>
</div>
</div>
</section>