add unibest app skeleton with checkin and progress pages

- init unibest v4.4.0 base template
- tabbar: 4 tabs (打卡/进度/测算/持仓) with carbon icons
- pages/checkin: daily checklist, streak counter, monthly rate
- pages/progress: asset snapshot, progress bar to 71万 target
- pages/calculator, holdings: placeholder pages (stage 2)
- store/checkin.ts + store/progress.ts with pinia persist
- vite: host 0.0.0.0 for WSL2→Windows browser access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ila
2026-06-08 10:46:43 +08:00
co-authored by Claude Sonnet 4.6
parent 9e7d20f062
commit a8fa0c0753
141 changed files with 29445 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
<script lang="ts" setup>
import { useProgressStore } from '@/store/progress'
defineOptions({ name: 'Progress' })
definePage({
style: { navigationBarTitleText: '目标进度' },
})
const store = useProgressStore()
const TARGET = 710000
const percent = computed(() => Math.min(100, Math.round((store.netAssets / TARGET) * 100)))
const remaining = computed(() => Math.max(0, TARGET - store.netAssets))
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>