2026-06-08 10:39:30 +08:00
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
|
import { useProgressStore } from '@/store/progress'
|
2026-06-08 11:01:16 +08:00
|
|
|
|
import { useSettingsStore } from '@/store/settings'
|
2026-06-08 10:39:30 +08:00
|
|
|
|
|
|
|
|
|
|
defineOptions({ name: 'Progress' })
|
|
|
|
|
|
definePage({
|
|
|
|
|
|
style: { navigationBarTitleText: '目标进度' },
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const store = useProgressStore()
|
2026-06-08 11:01:16 +08:00
|
|
|
|
const settings = useSettingsStore()
|
2026-06-08 10:39:30 +08:00
|
|
|
|
|
2026-06-08 11:01:16 +08:00
|
|
|
|
const percent = computed(() => Math.min(100, Math.round((store.netAssets / settings.target) * 100)))
|
|
|
|
|
|
const remaining = computed(() => Math.max(0, settings.target - store.netAssets))
|
2026-06-08 10:39:30 +08:00
|
|
|
|
|
|
|
|
|
|
const showForm = ref(false)
|
|
|
|
|
|
const inputVal = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
function saveSnapshot() {
|
|
|
|
|
|
const val = Number(inputVal.value)
|
|
|
|
|
|
if (!val || val < 0)
|
|
|
|
|
|
return
|
|
|
|
|
|
store.addSnapshot(val)
|
|
|
|
|
|
showForm.value = false
|
|
|
|
|
|
inputVal.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<view class="min-h-screen bg-gray-50 px-4 pt-12 pb-24">
|
|
|
|
|
|
<view class="mb-6 text-2xl font-bold text-gray-800">
|
|
|
|
|
|
目标进度
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 目标进度卡片 -->
|
|
|
|
|
|
<view class="mb-4 rounded-2xl bg-white p-5 shadow-sm">
|
|
|
|
|
|
<view class="mb-1 flex justify-between text-sm text-gray-500">
|
|
|
|
|
|
<text>当前净资产</text>
|
|
|
|
|
|
<text>Barista FIRE 目标 71 万</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<!-- 进度条 -->
|
|
|
|
|
|
<view class="my-3 h-4 rounded-full bg-gray-100 overflow-hidden">
|
|
|
|
|
|
<view
|
|
|
|
|
|
class="h-full rounded-full bg-green-500 transition-all"
|
|
|
|
|
|
:style="{ width: `${percent}%` }"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="flex justify-between">
|
|
|
|
|
|
<view class="text-2xl font-bold text-gray-800">
|
|
|
|
|
|
¥{{ store.netAssets.toLocaleString() }}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="text-xl font-bold text-green-600">
|
|
|
|
|
|
{{ percent }}%
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="mt-1 text-sm text-gray-400">
|
|
|
|
|
|
距目标还差 ¥{{ remaining.toLocaleString() }}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 统计卡片 -->
|
|
|
|
|
|
<view class="mb-4 grid grid-cols-2 gap-3">
|
|
|
|
|
|
<view class="rounded-2xl bg-white p-4 shadow-sm">
|
|
|
|
|
|
<view class="text-sm text-gray-500">
|
|
|
|
|
|
储蓄率
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="mt-1 text-2xl font-bold text-blue-500">
|
|
|
|
|
|
{{ store.savingsRate }}%
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="rounded-2xl bg-white p-4 shadow-sm">
|
|
|
|
|
|
<view class="text-sm text-gray-500">
|
|
|
|
|
|
累计记录次数
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="mt-1 text-2xl font-bold text-purple-500">
|
|
|
|
|
|
{{ store.snapshots.length }}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 净资产曲线(简单列表,图表后续加) -->
|
|
|
|
|
|
<view class="mb-4 rounded-2xl bg-white shadow-sm overflow-hidden">
|
|
|
|
|
|
<view class="px-5 py-3 border-b border-gray-100 font-medium text-gray-700">
|
|
|
|
|
|
历史记录
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-if="store.snapshots.length === 0" class="px-5 py-8 text-center text-gray-400">
|
|
|
|
|
|
暂无记录,点击下方按钮录入
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-for="s in [...store.snapshots].reverse().slice(0, 10)"
|
|
|
|
|
|
:key="s.date"
|
|
|
|
|
|
class="flex justify-between px-5 py-3 border-b border-gray-100 last:border-none"
|
|
|
|
|
|
>
|
|
|
|
|
|
<text class="text-gray-500 text-sm">{{ s.date }}</text>
|
|
|
|
|
|
<text class="font-medium text-gray-800">¥{{ s.amount.toLocaleString() }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 录入按钮 -->
|
|
|
|
|
|
<view
|
|
|
|
|
|
class="fixed bottom-20 right-5 h-14 w-14 rounded-full bg-green-500 shadow-lg flex items-center justify-center"
|
|
|
|
|
|
@tap="showForm = true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<text class="text-white text-2xl leading-none">+</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 录入弹窗 -->
|
|
|
|
|
|
<view v-if="showForm" class="fixed inset-0 bg-black/40 flex items-end" @tap.self="showForm = false">
|
|
|
|
|
|
<view class="w-full rounded-t-3xl bg-white p-6">
|
|
|
|
|
|
<view class="mb-4 text-lg font-medium text-gray-800">
|
|
|
|
|
|
录入当前净资产
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<input
|
|
|
|
|
|
v-model="inputVal"
|
|
|
|
|
|
type="digit"
|
|
|
|
|
|
placeholder="请输入金额(元)"
|
|
|
|
|
|
class="w-full rounded-xl border border-gray-200 px-4 py-3 text-base"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<view class="mt-4 flex gap-3">
|
|
|
|
|
|
<view
|
|
|
|
|
|
class="flex-1 rounded-xl border border-gray-200 py-3 text-center text-gray-500"
|
|
|
|
|
|
@tap="showForm = false"
|
|
|
|
|
|
>
|
|
|
|
|
|
取消
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view
|
|
|
|
|
|
class="flex-1 rounded-xl bg-green-500 py-3 text-center text-white font-medium"
|
|
|
|
|
|
@tap="saveSnapshot"
|
|
|
|
|
|
>
|
|
|
|
|
|
保存
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|