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:
@@ -4,6 +4,8 @@ import { request } from '@/utils/request'
|
||||
export interface LoginResult {
|
||||
token: string
|
||||
userId: number
|
||||
nickname: string
|
||||
avatar_url: string
|
||||
}
|
||||
|
||||
/** H5 演示登录 */
|
||||
|
||||
@@ -8,8 +8,8 @@ export interface Budget {
|
||||
}
|
||||
|
||||
/** 获取预算 */
|
||||
export function getBudget(month?: string) {
|
||||
return request<Budget | null>({ url: '/budget', data: month ? { month } : {} })
|
||||
export function getBudget(month?: string, group_id?: number | null) {
|
||||
return request<Budget | null>({ url: '/budget', data: { month, group_id } })
|
||||
}
|
||||
|
||||
/** 设置预算 */
|
||||
|
||||
58
client/src/api/group.ts
Normal file
58
client/src/api/group.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
/** 群组信息 */
|
||||
export interface Group {
|
||||
id: number
|
||||
name: string
|
||||
invite_code: string
|
||||
created_by: number
|
||||
created_at: string
|
||||
role: 'owner' | 'member'
|
||||
my_nickname: string
|
||||
member_count: number
|
||||
}
|
||||
|
||||
/** 群组详情(含成员) */
|
||||
export interface GroupDetail extends Group {
|
||||
members: GroupMember[]
|
||||
}
|
||||
|
||||
/** 群组成员 */
|
||||
export interface GroupMember {
|
||||
user_id: number
|
||||
role: 'owner' | 'member'
|
||||
nickname: string
|
||||
user_nickname: string
|
||||
avatar_url: string
|
||||
joined_at: string
|
||||
}
|
||||
|
||||
/** 创建群组 */
|
||||
export function createGroup(data: { name: string }) {
|
||||
return request<{ id: number; invite_code: string }>({ url: '/groups', method: 'POST', data })
|
||||
}
|
||||
|
||||
/** 获取群组列表 */
|
||||
export function getGroups() {
|
||||
return request<Group[]>({ url: '/groups' })
|
||||
}
|
||||
|
||||
/** 获取群组详情 */
|
||||
export function getGroupDetail(id: number) {
|
||||
return request<GroupDetail>({ url: `/groups/${id}` })
|
||||
}
|
||||
|
||||
/** 加入群组 */
|
||||
export function joinGroup(invite_code: string) {
|
||||
return request<{ group_id: number; name: string }>({ url: '/groups/join', method: 'POST', data: { invite_code } })
|
||||
}
|
||||
|
||||
/** 退出群组 */
|
||||
export function leaveGroup(id: number) {
|
||||
return request({ url: `/groups/${id}/leave`, method: 'POST' })
|
||||
}
|
||||
|
||||
/** 解散群组 */
|
||||
export function deleteGroup(id: number) {
|
||||
return request({ url: `/groups/${id}`, method: 'DELETE' })
|
||||
}
|
||||
@@ -25,16 +25,16 @@ export interface TrendPoint {
|
||||
}
|
||||
|
||||
/** 获取概览数据 */
|
||||
export function getOverview(params?: { month?: string; startDate?: string; endDate?: string }) {
|
||||
export function getOverview(params?: { month?: string; startDate?: string; endDate?: string; group_id?: number | null }) {
|
||||
return request<Overview>({ url: '/stats/overview', data: params })
|
||||
}
|
||||
|
||||
/** 获取分类统计 */
|
||||
export function getCategoryStats(month?: string, type: string = 'expense') {
|
||||
return request<CategoryStat[]>({ url: '/stats/category', data: { month, type } })
|
||||
export function getCategoryStats(month?: string, type: string = 'expense', group_id?: number | null) {
|
||||
return request<CategoryStat[]>({ url: '/stats/category', data: { month, type, group_id } })
|
||||
}
|
||||
|
||||
/** 获取趋势数据 */
|
||||
export function getTrend(month?: string, type: string = 'expense') {
|
||||
return request<TrendPoint[]>({ url: '/stats/trend', data: { month, type } })
|
||||
export function getTrend(month?: string, type: string = 'expense', group_id?: number | null) {
|
||||
return request<TrendPoint[]>({ url: '/stats/trend', data: { month, type, group_id } })
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ export interface Transaction {
|
||||
category_name?: string
|
||||
category_icon?: string
|
||||
category_color?: string
|
||||
group_id?: number | null
|
||||
creator_nickname?: string
|
||||
}
|
||||
|
||||
/** 交易列表响应 */
|
||||
@@ -29,6 +31,7 @@ export interface TransactionParams {
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
sortBy?: string
|
||||
group_id?: number | null
|
||||
}
|
||||
|
||||
/** 获取交易列表 */
|
||||
@@ -48,6 +51,7 @@ export function createTransaction(data: {
|
||||
category_id?: number
|
||||
note?: string
|
||||
date: string
|
||||
group_id?: number | null
|
||||
}) {
|
||||
return request<{ id: number }>({ url: '/transactions', method: 'POST', data })
|
||||
}
|
||||
|
||||
46
client/src/api/user.ts
Normal file
46
client/src/api/user.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { request } from '@/utils/request'
|
||||
import { API_BASE } from '@/config'
|
||||
|
||||
/** 用户信息 */
|
||||
export interface UserInfo {
|
||||
id: number
|
||||
nickname: string
|
||||
avatar_url: string
|
||||
created_at: string
|
||||
}
|
||||
|
||||
/** 获取当前用户信息 */
|
||||
export function getUserInfo() {
|
||||
return request<UserInfo>({ url: '/user/me' })
|
||||
}
|
||||
|
||||
/** 更新昵称 */
|
||||
export function updateNickname(nickname: string) {
|
||||
return request({ url: '/user/me', method: 'PUT', data: { nickname } })
|
||||
}
|
||||
|
||||
/** 上传头像(返回新 URL) */
|
||||
export function uploadAvatar(filePath: string): Promise<{ avatar_url: string }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const token = uni.getStorageSync('xc:token')
|
||||
uni.uploadFile({
|
||||
url: `${API_BASE}/user/avatar`,
|
||||
filePath,
|
||||
name: 'file',
|
||||
header: { Authorization: `Bearer ${token}` },
|
||||
success: (res) => {
|
||||
try {
|
||||
const data = JSON.parse(res.data)
|
||||
if (data.code === 0) {
|
||||
resolve(data.data)
|
||||
} else {
|
||||
reject(new Error(data.message || '上传失败'))
|
||||
}
|
||||
} catch {
|
||||
reject(new Error('上传响应解析失败'))
|
||||
}
|
||||
},
|
||||
fail: () => reject(new Error('网络错误'))
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user