feat: 用户信息完善 + 一起记功能
- users 表添加 nickname、avatar_url 字段 - 用户信息 API(GET/PUT /me)+ 头像上传(multer) - 头像通过 API 路由获取(公开,不经过 auth) - 登录接口返回用户昵称和头像 - 用户信息 Store + 个人资料编辑页面 - 我的页面和首页显示真实用户信息 - 群组表(groups、group_members)+ transactions 添加 group_id - 群组 API(创建/加入/退出/解散) - 交易和统计 API 支持 group_id 视图切换 - 群组客户端 Store + 身份切换 - 群组管理页面 - App 初始化群组 Store - UPLOAD_DIR 配置化 - Node.js 备份替代 mysqldump - 统一前端 BASE_URL(config.ts) - uploads 加入 .gitignore - 修复 trust proxy 警告
This commit is contained in:
@@ -2,11 +2,12 @@
|
||||
<view class="page">
|
||||
<view class="header-fixed">
|
||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||||
<view class="header">
|
||||
<view class="header" :style="{ paddingRight: capsuleRight + 'px' }">
|
||||
<view class="avatar-wrap">
|
||||
<Icon name="user" :size="36" color="#8B7E7E" />
|
||||
<image v-if="userStore.avatarUrl" :src="userStore.avatarUrl" class="avatar-img" mode="aspectFill" />
|
||||
<Icon v-else name="user" :size="36" color="#8B7E7E" />
|
||||
</view>
|
||||
<text class="greeting">{{ greeting }},小菜</text>
|
||||
<text class="greeting">{{ greeting }},{{ userStore.nickname }}</text>
|
||||
<view class="bell" @tap="showNotification">
|
||||
<Icon name="bell" :size="40" color="#8B7E7E" />
|
||||
</view>
|
||||
@@ -117,9 +118,12 @@ import { onShow, onPullDownRefresh } from '@dcloudio/uni-app'
|
||||
import { useTransactionStore } from '@/stores/transaction'
|
||||
import { useStatsStore } from '@/stores/stats'
|
||||
import { useBudgetStore } from '@/stores/budget'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useGroupStore } from '@/stores/group'
|
||||
import { waitForReady } from '@/utils/app-ready'
|
||||
import { getOverview } from '@/api/stats'
|
||||
import { formatAmount, formatAmountRaw } from '@/utils/format'
|
||||
import { statusBarHeight } from '@/utils/system'
|
||||
import { statusBarHeight, capsuleRight } from '@/utils/system'
|
||||
import BudgetBar from '@/components/BudgetBar/BudgetBar.vue'
|
||||
import TransactionItem from '@/components/TransactionItem/TransactionItem.vue'
|
||||
import Icon from '@/components/Icon/Icon.vue'
|
||||
@@ -128,11 +132,13 @@ import Skeleton from '@/components/Skeleton/Skeleton.vue'
|
||||
const txStore = useTransactionStore()
|
||||
const statsStore = useStatsStore()
|
||||
const budgetStore = useBudgetStore()
|
||||
const userStore = useUserStore()
|
||||
const groupStore = useGroupStore()
|
||||
|
||||
const DEFAULT_BUDGET = 500000 // 5000元(分)
|
||||
|
||||
const overview = ref({ expense: 0, income: 0, count: 0, daily: 0 })
|
||||
const budget = ref<any>(null)
|
||||
const overview = computed(() => statsStore.overview)
|
||||
const budget = computed(() => budgetStore.budget)
|
||||
const recentTx = ref<any[]>([])
|
||||
const loading = ref(true)
|
||||
const loadError = ref(false)
|
||||
@@ -149,9 +155,10 @@ const greeting = computed(() => {
|
||||
return '晚上好'
|
||||
})
|
||||
|
||||
async function loadData() {
|
||||
async function loadData(silent = false) {
|
||||
await waitForReady()
|
||||
try {
|
||||
loading.value = true
|
||||
if (!silent) loading.value = true
|
||||
loadError.value = false
|
||||
const now = new Date()
|
||||
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
||||
@@ -159,14 +166,13 @@ async function loadData() {
|
||||
statsStore.fetchOverview(),
|
||||
budgetStore.fetchBudget(),
|
||||
txStore.fetchTransactions({ page: 1 }),
|
||||
fetchTodayData(today)
|
||||
fetchTodayData(today),
|
||||
userStore.fetchUserInfo()
|
||||
])
|
||||
overview.value = statsStore.overview
|
||||
budget.value = budgetStore.budget
|
||||
recentTx.value = txStore.transactions.slice(0, 5)
|
||||
} catch (e) {
|
||||
console.error('Load error:', e)
|
||||
loadError.value = true
|
||||
if (!silent) loadError.value = true
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -174,7 +180,7 @@ async function loadData() {
|
||||
|
||||
async function fetchTodayData(today: string) {
|
||||
try {
|
||||
const data = await getOverview({ startDate: today, endDate: today })
|
||||
const data = await getOverview({ startDate: today, endDate: today, group_id: groupStore.currentGroupId })
|
||||
todayExpense.value = data.expense || 0
|
||||
todayIncome.value = data.income || 0
|
||||
todayCount.value = data.count || 0
|
||||
@@ -192,7 +198,7 @@ onMounted(() => {
|
||||
|
||||
// Refresh data when returning from other pages (e.g., after adding a transaction)
|
||||
onShow(() => {
|
||||
if (initialLoaded.value && !loading.value) loadData()
|
||||
if (initialLoaded.value && !loading.value) loadData(true)
|
||||
})
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
@@ -249,6 +255,12 @@ function goBills() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.greeting {
|
||||
|
||||
Reference in New Issue
Block a user