feat: 添加通知设置功能,支持用户开启/关闭通知捕获,优化设置页功能显示,升级kotlin构建版本
This commit is contained in:
@@ -18,7 +18,7 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
// <EFBFBD><EFBFBD>ʽͳһ Java / Kotlin <EFBFBD>ı<EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><EFBFBD><EFBFBD>汾<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Capacitor <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD>£<EFBFBD>Java 21<EFBFBD><EFBFBD>
|
||||
// 显式统一 Java / Kotlin 的编译目标版本,保持与 Capacitor 生成配置一致(Java 21)
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_21
|
||||
targetCompatibility JavaVersion.VERSION_21
|
||||
@@ -52,4 +52,5 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
}
|
||||
|
||||
apply from: 'capacitor.build.gradle'
|
||||
apply from: 'capacitor.build.gradle'
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.echo.app.notification
|
||||
package com.echo.app.notification
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.provider.Settings
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.getcapacitor.JSArray
|
||||
import com.getcapacitor.JSObject
|
||||
@@ -12,7 +13,7 @@ import com.getcapacitor.PluginCall
|
||||
import com.getcapacitor.PluginMethod
|
||||
import com.getcapacitor.annotation.CapacitorPlugin
|
||||
|
||||
// Capacitor <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD>˱<EFBFBD>¶֪ͨȨ<EFBFBD><EFBFBD><EFBFBD>顢<EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD>ȡ<EFBFBD><EFBFBD><EFBFBD>Լ<EFBFBD>֪ͨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD>
|
||||
// Capacitor 插件:向前端暴露通知权限、通知队列以及通知事件
|
||||
@CapacitorPlugin(name = "NotificationBridge")
|
||||
class NotificationBridgePlugin : Plugin() {
|
||||
|
||||
@@ -21,7 +22,7 @@ class NotificationBridgePlugin : Plugin() {
|
||||
override fun load() {
|
||||
super.load()
|
||||
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> NotificationBridgeService <EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD><EFBFBD>ڹ㲥<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><EFBFBD>Ϊ JS <EFBFBD>¼<EFBFBD>
|
||||
// 监听 NotificationBridgeService 发送的应用内广播,并转发为 JS 事件
|
||||
val filter = IntentFilter("com.echo.app.NOTIFICATION_POSTED")
|
||||
notificationPostedReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
@@ -52,10 +53,19 @@ class NotificationBridgePlugin : Plugin() {
|
||||
|
||||
@PluginMethod
|
||||
fun requestPermission(call: PluginCall) {
|
||||
// <EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><EFBFBD>ϵͳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// 当前仅返回权限状态,实际的引导文案和跳转由前端控制
|
||||
hasPermission(call)
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
fun openNotificationSettings(call: PluginCall) {
|
||||
// 跳转到系统的“通知使用权”设置页面,引导用户手动开启监听权限
|
||||
val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(intent)
|
||||
call.resolve()
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
fun getPendingNotifications(call: PluginCall) {
|
||||
val queue = NotificationStorage.list(context)
|
||||
@@ -80,4 +90,4 @@ class NotificationBridgePlugin : Plugin() {
|
||||
NotificationStorage.clear(context)
|
||||
call.resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<resources>
|
||||
<!-- <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>⣬<EFBFBD><EFBFBD><EFBFBD>ں<EFBFBD> Capacitor Ĭ<><C4AC><EFBFBD><EFBFBD>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD> -->
|
||||
<resources>
|
||||
<!-- 应用主主题:为 Capacitor WebView 启用沉浸式状态栏和导航栏 -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<!-- <EFBFBD><EFBFBD><EFBFBD><EFBFBD> Activity ר<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>⣨Ŀǰ<EFBFBD><EFBFBD> AppTheme һ<>£<EFBFBD><C2A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɽ<EFBFBD><C9BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ҳ<EFBFBD><D2B3> -->
|
||||
<!-- 启动 Activity 专用主题(当前与 AppTheme 一致,后续可单独定制) -->
|
||||
<style name="AppTheme.NoActionBarLaunch" parent="AppTheme" />
|
||||
</resources>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.8.22'
|
||||
ext.kotlin_version = '2.0.0'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.0.0'
|
||||
classpath 'com.android.tools.build:gradle:8.4.2'
|
||||
classpath 'com.google.gms:google-services:4.3.15'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
@@ -26,3 +26,5 @@ allprojects {
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
|
||||
|
||||
10
src/App.vue
10
src/App.vue
@@ -1,4 +1,4 @@
|
||||
<script setup>
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { RouterView } from 'vue-router'
|
||||
import BottomDock from './components/BottomDock.vue'
|
||||
@@ -12,20 +12,18 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 整体应用容器:居中展示移动端画布 -->
|
||||
<!-- 整体应用容器:居中展示移动端画布,配合沉浸式状态栏使用安全区域内边距 -->
|
||||
<div class="min-h-screen bg-warmOffwhite text-stone-800 flex items-center justify-center">
|
||||
<div
|
||||
class="h-screen max-h-[844px] max-w-md w-full mx-auto bg-warmOffwhite shadow-2xl relative overflow-hidden flex flex-col"
|
||||
style="padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom);"
|
||||
>
|
||||
<!-- 顶部状态栏占位 -->
|
||||
<div class="h-8 w-full shrink-0" />
|
||||
|
||||
<!-- 主内容区域:由 vue-router 控制具体页面 -->
|
||||
<main class="flex-1 overflow-y-auto hide-scrollbar px-5 pt-2 pb-28 relative">
|
||||
<RouterView />
|
||||
</main>
|
||||
|
||||
<!-- 底部 Dock 导航栏 -->
|
||||
<!-- 底部 Dock 导航 -->
|
||||
<BottomDock />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
transformNotificationToTransaction,
|
||||
} from '../services/notificationRuleService'
|
||||
import NotificationBridge, { isNativeNotificationBridgeAvailable } from '../lib/notificationBridge'
|
||||
import { useSettingsStore } from '../stores/settings'
|
||||
|
||||
const notifications = ref([])
|
||||
const processingId = ref('')
|
||||
@@ -35,6 +36,7 @@ const ensureRulesReady = async () => {
|
||||
|
||||
export const useTransactionEntry = () => {
|
||||
const transactionStore = useTransactionStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
// 原生端:在首次同步前做一次权限校验与请求
|
||||
const ensureNativePermission = async () => {
|
||||
@@ -83,6 +85,11 @@ export const useTransactionEntry = () => {
|
||||
if (syncing.value) return
|
||||
syncing.value = true
|
||||
try {
|
||||
// 若用户关闭了通知自动捕获,则不从原生/模拟队列拉取新通知,只清空待确认列表
|
||||
if (!settingsStore.notificationCaptureEnabled) {
|
||||
notifications.value = []
|
||||
return
|
||||
}
|
||||
if (nativeBridgeReady) {
|
||||
await ensureNativePermission()
|
||||
}
|
||||
|
||||
31
src/stores/settings.js
Normal file
31
src/stores/settings.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export const useSettingsStore = defineStore(
|
||||
'settings',
|
||||
() => {
|
||||
const notificationCaptureEnabled = ref(true)
|
||||
const aiAutoCategoryEnabled = ref(false)
|
||||
|
||||
const setNotificationCaptureEnabled = (value) => {
|
||||
notificationCaptureEnabled.value = !!value
|
||||
}
|
||||
|
||||
const setAiAutoCategoryEnabled = (value) => {
|
||||
aiAutoCategoryEnabled.value = !!value
|
||||
}
|
||||
|
||||
return {
|
||||
notificationCaptureEnabled,
|
||||
aiAutoCategoryEnabled,
|
||||
setNotificationCaptureEnabled,
|
||||
setAiAutoCategoryEnabled,
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: {
|
||||
paths: ['notificationCaptureEnabled', 'aiAutoCategoryEnabled'],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,66 +1,184 @@
|
||||
<script setup>
|
||||
// 设置页当前只展示静态 UI,后续会接入真实设置状态和持久化逻辑
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import NotificationBridge, { isNativeNotificationBridgeAvailable } from '../lib/notificationBridge'
|
||||
import { useSettingsStore } from '../stores/settings'
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
const notificationCaptureEnabled = computed({
|
||||
get: () => settingsStore.notificationCaptureEnabled,
|
||||
set: (value) => settingsStore.setNotificationCaptureEnabled(value),
|
||||
})
|
||||
|
||||
const aiAutoCategoryEnabled = computed({
|
||||
get: () => settingsStore.aiAutoCategoryEnabled,
|
||||
set: (value) => settingsStore.setAiAutoCategoryEnabled(value),
|
||||
})
|
||||
|
||||
const nativeBridgeReady = isNativeNotificationBridgeAvailable()
|
||||
const notificationPermissionGranted = ref(true)
|
||||
const checkingPermission = ref(false)
|
||||
|
||||
const checkNotificationPermission = async () => {
|
||||
if (!nativeBridgeReady) return
|
||||
checkingPermission.value = true
|
||||
try {
|
||||
const result = await NotificationBridge.hasPermission()
|
||||
notificationPermissionGranted.value = !!result?.granted
|
||||
} catch {
|
||||
notificationPermissionGranted.value = false
|
||||
} finally {
|
||||
checkingPermission.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const toggleNotificationCapture = async () => {
|
||||
const next = !notificationCaptureEnabled.value
|
||||
notificationCaptureEnabled.value = next
|
||||
if (next && nativeBridgeReady) {
|
||||
await checkNotificationPermission()
|
||||
if (!notificationPermissionGranted.value) {
|
||||
try {
|
||||
await NotificationBridge.openNotificationSettings()
|
||||
} catch {
|
||||
// 插件调用失败时忽略,由下方文案提示用户
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const toggleAiAutoCategory = () => {
|
||||
aiAutoCategoryEnabled.value = !aiAutoCategoryEnabled.value
|
||||
}
|
||||
|
||||
const handleExportData = () => {
|
||||
window.alert('导出功能即将上线:届时可以一键导出 CSV / Excel。当前版本建议先通过截图或复制方式备份关键信息。')
|
||||
}
|
||||
|
||||
const handleClearCache = () => {
|
||||
const ok = window.confirm('确认清除缓存吗?这不会删除正式账本数据,但会重置筛选偏好等本地设置。')
|
||||
if (!ok) return
|
||||
try {
|
||||
// 暂时只清理本地设置缓存,避免误删账本
|
||||
localStorage.removeItem('settings')
|
||||
window.alert('已清除设置缓存,部分偏好将在下次启动时重置。')
|
||||
} catch {
|
||||
window.alert('清除缓存失败,可稍后重试。')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void checkNotificationPermission()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-6 animate-fade-in pb-10">
|
||||
<h2 class="text-2xl font-extrabold text-stone-800">设置</h2>
|
||||
|
||||
<!-- Profile Section -->
|
||||
<!-- 个人信息 / 简短说明 -->
|
||||
<div class="bg-white rounded-3xl p-5 flex items-center gap-4 shadow-sm border border-stone-100">
|
||||
<img
|
||||
src="https://api.dicebear.com/7.x/avataaars/svg?seed=Felix"
|
||||
src="https://api.dicebear.com/7.x/avataaars/svg?seed=Echo"
|
||||
alt="Avatar"
|
||||
class="w-16 h-16 rounded-full bg-stone-100"
|
||||
/>
|
||||
<div class="flex-1">
|
||||
<h3 class="font-bold text-lg text-stone-800">Alex Chen</h3>
|
||||
<p class="text-xs text-stone-400">Pro 会员 (2025.12 到期)</p>
|
||||
<h3 class="font-bold text-lg text-stone-800">Echo 用户</h3>
|
||||
<p class="text-xs text-stone-400">本地优先 · 数据只存这台设备</p>
|
||||
</div>
|
||||
<button
|
||||
class="w-8 h-8 rounded-full bg-stone-50 flex items-center justify-center text-stone-400 active:bg-stone-100 transition"
|
||||
>
|
||||
<i class="ph-bold ph-pencil-simple" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Settings Group 1: Automation & AI -->
|
||||
<!-- 自动化 & AI -->
|
||||
<section>
|
||||
<h3 class="text-xs font-bold text-stone-400 uppercase tracking-widest mb-3 ml-2">
|
||||
自动化 & AI
|
||||
</h3>
|
||||
<div class="bg-white rounded-3xl overflow-hidden shadow-sm border border-stone-100">
|
||||
<div class="flex items-center justify-between p-4 border-b border-stone-50">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center"
|
||||
>
|
||||
<i class="ph-fill ph-bell-ringing" />
|
||||
<!-- 自动捕获支付通知 -->
|
||||
<div class="flex flex-col gap-1 border-b border-stone-50">
|
||||
<div class="flex items-center justify-between p-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 rounded-full bg-blue-50 text-blue-500 flex items-center justify-center"
|
||||
>
|
||||
<i class="ph-fill ph-bell-ringing" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-bold text-stone-700 text-sm">自动捕获支付通知</p>
|
||||
<p class="text-[11px] text-stone-400 mt-0.5">
|
||||
监听支付宝 / 微信 / 银行 App 通知,在本机自动生成记账草稿。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="font-bold text-stone-700 text-sm">自动捕获支付通知</span>
|
||||
<button
|
||||
class="w-11 h-6 rounded-full px-0.5 flex items-center transition-all duration-200"
|
||||
:class="
|
||||
notificationCaptureEnabled
|
||||
? 'bg-orange-400 justify-end'
|
||||
: 'bg-stone-200 justify-start'
|
||||
"
|
||||
@click="toggleNotificationCapture"
|
||||
>
|
||||
<div class="w-4 h-4 bg-white rounded-full shadow-sm" />
|
||||
</button>
|
||||
</div>
|
||||
<!-- 开关目前为静态 UI,后续接入真实状态 -->
|
||||
<div class="w-11 h-6 bg-orange-400 rounded-full relative cursor-pointer">
|
||||
<div class="absolute right-1 top-1 w-4 h-4 bg-white rounded-full shadow-sm" />
|
||||
|
||||
<div v-if="notificationCaptureEnabled" class="px-4 pb-4">
|
||||
<div
|
||||
v-if="!notificationPermissionGranted"
|
||||
class="bg-orange-50 border border-orange-200 rounded-2xl px-3 py-2 flex items-start gap-2"
|
||||
>
|
||||
<i class="ph-bold ph-warning text-orange-500 mt-0.5" />
|
||||
<div class="text-[11px] text-orange-700 leading-relaxed">
|
||||
<p>系统尚未授予 Echo「通知使用权」,否则无法自动捕获通知。</p>
|
||||
<button
|
||||
class="mt-1 text-[11px] font-bold text-orange-600 underline"
|
||||
@click="NotificationBridge.openNotificationSettings()"
|
||||
>
|
||||
去系统设置开启
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="text-[11px] text-emerald-600 flex items-center gap-1">
|
||||
<i class="ph-bold ph-check-circle" />
|
||||
通知权限已开启,Echo 会在收到新通知后自动刷新首页。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between p-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 rounded-full bg-purple-50 text-purple-500 flex items-center justify-center"
|
||||
>
|
||||
<i class="ph-fill ph-magic-wand" />
|
||||
|
||||
<!-- AI 自动分类开关(预留) -->
|
||||
<div class="flex flex-col gap-1 p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 rounded-full bg-purple-50 text-purple-500 flex items-center justify-center"
|
||||
>
|
||||
<i class="ph-fill ph-magic-wand" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-bold text-stone-700 text-sm">AI 自动分类(预留)</p>
|
||||
<p class="text-[11px] text-stone-400 mt-0.5">
|
||||
开启后,未来会优先使用云端 AI 对商户进行分类与标签分析。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="font-bold text-stone-700 text-sm">AI 自动分类</span>
|
||||
</div>
|
||||
<div class="w-11 h-6 bg-orange-400 rounded-full relative cursor-pointer">
|
||||
<div class="absolute right-1 top-1 w-4 h-4 bg-white rounded-full shadow-sm" />
|
||||
<button
|
||||
class="w-11 h-6 rounded-full px-0.5 flex items-center transition-all duration-200"
|
||||
:class="aiAutoCategoryEnabled ? 'bg-orange-400 justify-end' : 'bg-stone-200 justify-start'"
|
||||
@click="toggleAiAutoCategory"
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Settings Group 2: General -->
|
||||
<!-- 通用设置 -->
|
||||
<section>
|
||||
<h3 class="text-xs font-bold text-stone-400 uppercase tracking-widest mb-3 ml-2">
|
||||
通用
|
||||
@@ -68,6 +186,7 @@
|
||||
<div class="bg-white rounded-3xl overflow-hidden shadow-sm border border-stone-100">
|
||||
<button
|
||||
class="flex w-full items-center justify-between p-4 border-b border-stone-50 active:bg-stone-50 transition text-left"
|
||||
@click="handleExportData"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
@@ -75,27 +194,35 @@
|
||||
>
|
||||
<i class="ph-fill ph-export" />
|
||||
</div>
|
||||
<span class="font-bold text-stone-700 text-sm">导出账单数据 (Excel)</span>
|
||||
<div>
|
||||
<p class="font-bold text-stone-700 text-sm">导出账单数据(预留)</p>
|
||||
<p class="text-[11px] text-stone-400 mt-0.5">未来支持导出为 CSV / Excel 文件。</p>
|
||||
</div>
|
||||
</div>
|
||||
<i class="ph-bold ph-caret-right text-stone-300" />
|
||||
</button>
|
||||
<div class="flex items-center justify-between p-4 active:bg-stone-50 transition">
|
||||
<button
|
||||
class="flex w-full items-center justify-between p-4 active:bg-stone-50 transition text-left"
|
||||
@click="handleClearCache"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 rounded-full bg-red-50 text-red-500 flex items-center justify-center"
|
||||
>
|
||||
<i class="ph-fill ph-trash" />
|
||||
</div>
|
||||
<span class="font-bold text-stone-700 text-sm">清除缓存</span>
|
||||
<div>
|
||||
<p class="font-bold text-stone-700 text-sm">清除缓存(设置)</p>
|
||||
<p class="text-[11px] text-stone-400 mt-0.5">仅清理本地偏好,不影响正式账本数据。</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-xs text-stone-400">128 MB</span>
|
||||
</div>
|
||||
<span class="text-xs text-stone-400">≈ 数 KB</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="text-center pt-4">
|
||||
<p class="text-xs text-stone-300">Version 1.0.2 Beta</p>
|
||||
<p class="text-xs text-stone-300">Echo · Local-first Beta</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user