first commot
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 8s
Docker Build & Deploy / Deploy to Production (push) Successful in 7s

This commit is contained in:
孙诚
2025-12-25 11:20:56 +08:00
commit 4526cc6396
104 changed files with 11070 additions and 0 deletions

68
Web/src/api/billImport.js Normal file
View File

@@ -0,0 +1,68 @@
import axios from 'axios'
import { showToast } from 'vant'
/**
* 账单导入相关 API
*/
/**
* 上传账单文件
* @param {File} file - 要上传的文件
* @param {string} type - 账单类型 ('Alipay' | 'WeChat')
* @returns {Promise<{success: boolean, message: string, data: any}>}
*/
export const uploadBillFile = (file, type) => {
const formData = new FormData()
formData.append('file', file)
formData.append('type', type)
return axios({
url: `${import.meta.env.VITE_API_BASE_URL}/BillImport/UploadFile`,
method: 'post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
},
timeout: 60000 // 文件上传增加超时时间
}).then(response => {
const { data } = response
if (data.success === false) {
showToast(data.message || '上传失败')
return Promise.reject(new Error(data.message || '上传失败'))
}
return data
}).catch(error => {
console.error('上传错误:', error)
if (error.response) {
const { status, data } = error.response
let message = '上传失败'
switch (status) {
case 400:
message = data?.message || '请求参数错误'
break
case 401:
message = '未授权,请先登录'
break
case 403:
message = '没有权限'
break
case 413:
message = '文件过大'
break
case 500:
message = '服务器错误'
break
}
showToast(message)
return Promise.reject(new Error(message))
}
showToast('网络错误,请检查网络连接')
return Promise.reject(error)
})
}

View File

@@ -0,0 +1,80 @@
import request from './request'
/**
* 邮件记录相关 API
*/
/**
* 获取邮件列表(分页)
* @param {Object} params - 查询参数
* @param {number} [params.latestId] - 最后一条记录的ID用于游标分页
* @returns {Promise<{success: boolean, data: Array, total: number, lastId: number}>}
*/
export const getEmailList = (params = {}) => {
return request({
url: '/EmailMessage/GetList',
method: 'get',
params
})
}
/**
* 根据ID获取邮件详情
* @param {number} id - 邮件ID
* @returns {Promise<{success: boolean, data: Object}>}
*/
export const getEmailDetail = (id) => {
return request({
url: `/EmailMessage/GetById/${id}`,
method: 'get'
})
}
/**
* 删除邮件
* @param {number} id - 邮件ID
* @returns {Promise<{success: boolean}>}
*/
export const deleteEmail = (id) => {
return request({
url: `/EmailMessage/DeleteById`,
method: 'post',
params: { id }
})
}
/**
* 重新分析邮件并刷新交易记录
* @param {number} id - 邮件ID
* @returns {Promise<{success: boolean}>}
*/
export const refreshTransactionRecords = (id) => {
return request({
url: `/EmailMessage/RefreshTransactionRecords`,
method: 'post',
params: { id }
})
}
/**
* 立即同步邮件
* @returns {Promise<{success: boolean, message: string}>}
*/
export const syncEmails = () => {
return request({
url: `/EmailMessage/SyncEmails`,
method: 'post'
})
}
/**
* 获取邮件关联的交易记录列表
* @param {number} emailId - 邮件ID
* @returns {Promise<{success: boolean, data: Array}>}
*/
export const getEmailTransactions = (emailId) => {
return request({
url: `/TransactionRecord/GetByEmailId/${emailId}`,
method: 'get'
})
}

81
Web/src/api/request.js Normal file
View File

@@ -0,0 +1,81 @@
import axios from 'axios'
import { showToast } from 'vant'
// 创建 axios 实例
const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 30000,
headers: {
'Content-Type': 'application/json'
}
})
// 请求拦截器
request.interceptors.request.use(
config => {
// 可以在这里添加 token 等认证信息
// const token = localStorage.getItem('token')
// if (token) {
// config.headers.Authorization = `Bearer ${token}`
// }
return config
},
error => {
console.error('请求错误:', error)
return Promise.reject(error)
}
)
// 响应拦截器
request.interceptors.response.use(
response => {
const { data } = response
// 统一处理业务错误
if (data.success === false) {
showToast(data.message || '请求失败')
return Promise.reject(new Error(data.message || '请求失败'))
}
return data
},
error => {
console.error('响应错误:', error)
// 统一处理 HTTP 错误
if (error.response) {
const { status, data } = error.response
let message = '请求失败'
switch (status) {
case 400:
message = data?.message || '请求参数错误'
break
case 401:
message = '未授权,请重新登录'
break
case 403:
message = '拒绝访问'
break
case 404:
message = '请求的资源不存在'
break
case 500:
message = '服务器内部错误'
break
default:
message = data?.message || `请求失败 (${status})`
}
showToast(message)
} else if (error.request) {
showToast('网络连接失败,请检查网络')
} else {
showToast(error.message || '请求失败')
}
return Promise.reject(error)
}
)
export default request

View File

@@ -0,0 +1,104 @@
import request from './request'
/**
* 获取分类树(支持按类型筛选)
* @param {string|null} type - 交易类型(Expense=0/Income=1)null表示获取全部
* @returns {Promise<{success: boolean, data: Array}>}
*/
export const getCategoryTree = (type = null) => {
return request({
url: '/TransactionCategory/GetTree',
method: 'get',
params: type !== null ? { type } : {}
})
}
/**
* 获取顶级分类列表(按类型)
* @param {number} type - 交易类型(Expense=0/Income=1)
* @returns {Promise<{success: boolean, data: Array}>}
*/
export const getTopLevelCategories = (type) => {
return request({
url: '/TransactionCategory/GetTopLevel',
method: 'get',
params: { type }
})
}
/**
* 获取子分类列表
* @param {number} parentId - 父分类ID
* @returns {Promise<{success: boolean, data: Array}>}
*/
export const getChildCategories = (parentId) => {
return request({
url: '/TransactionCategory/GetChildren',
method: 'get',
params: { parentId }
})
}
/**
* 根据ID获取分类详情
* @param {number} id - 分类ID
* @returns {Promise<{success: boolean, data: object}>}
*/
export const getCategoryById = (id) => {
return request({
url: `/TransactionCategory/GetById/${id}`,
method: 'get'
})
}
/**
* 创建分类
* @param {object} data - 分类数据
* @returns {Promise<{success: boolean, data: number}>} 返回新创建的分类ID
*/
export const createCategory = (data) => {
return request({
url: '/TransactionCategory/Create',
method: 'post',
data
})
}
/**
* 更新分类
* @param {object} data - 分类数据
* @returns {Promise<{success: boolean}>}
*/
export const updateCategory = (data) => {
return request({
url: '/TransactionCategory/Update',
method: 'post',
data
})
}
/**
* 删除分类
* @param {number} id - 分类ID
* @returns {Promise<{success: boolean}>}
*/
export const deleteCategory = (id) => {
return request({
url: '/TransactionCategory/Delete',
method: 'post',
params: { id }
})
}
/**
* 批量创建分类(用于初始化)
* @param {Array} dataList - 分类数据数组
* @returns {Promise<{success: boolean, data: number}>} 返回创建的数量
*/
export const batchCreateCategories = (dataList) => {
return request({
url: '/TransactionCategory/BatchCreate',
method: 'post',
data: dataList
})
}

View File

@@ -0,0 +1,102 @@
import request from './request'
/**
* 交易记录相关 API
*/
/**
* 获取交易记录列表(分页)
* @param {Object} params - 查询参数
* @param {number} [params.latestId] - 最后一条记录的ID用于游标分页
* @param {string} [params.searchKeyword] - 搜索关键词
* @returns {Promise<{success: boolean, data: Array, total: number, lastId: number}>}
*/
export const getTransactionList = (params = {}) => {
return request({
url: '/TransactionRecord/GetList',
method: 'get',
params
})
}
/**
* 根据ID获取交易记录详情
* @param {number} id - 交易记录ID
* @returns {Promise<{success: boolean, data: Object}>}
*/
export const getTransactionDetail = (id) => {
return request({
url: `/TransactionRecord/GetById/${id}`,
method: 'get'
})
}
/**
* 创建交易记录
* @param {Object} data - 交易记录数据
* @param {string} data.card - 卡号
* @param {string} data.occurredAt - 交易时间
* @param {string} data.reason - 交易摘要
* @param {number} data.amount - 交易金额
* @param {number} data.balance - 交易后余额
* @param {number} data.type - 交易类型 (0:支出, 1:收入, 2:不计入收支)
* @param {string} data.classify - 交易分类
* @param {string} data.subClassify - 交易子分类
* @returns {Promise<{success: boolean}>}
*/
export const createTransaction = (data) => {
return request({
url: '/TransactionRecord/Create',
method: 'post',
data
})
}
/**
* 更新交易记录
* @param {Object} data - 交易记录数据
* @param {number} data.id - 交易记录ID
* @param {number} data.amount - 交易金额
* @param {number} data.balance - 交易后余额
* @param {number} data.type - 交易类型 (0:支出, 1:收入, 2:不计入收支)
* @param {string} data.classify - 交易分类
* @param {string} data.subClassify - 交易子分类
* @returns {Promise<{success: boolean}>}
*/
export const updateTransaction = (data) => {
return request({
url: '/TransactionRecord/Update',
method: 'post',
data
})
}
/**
* 删除交易记录
* @param {number} id - 交易记录ID
* @returns {Promise<{success: boolean}>}
*/
export const deleteTransaction = (id) => {
return request({
url: `/TransactionRecord/DeleteById`,
method: 'post',
params: { id }
})
}
/**
* 获取指定日期的交易记录
* @param {string} date - 日期字符串 (格式: yyyy-MM-dd)
* @returns {Promise<{success: boolean, data: Array}>}
*/
export const getTransactionsByDate = (date) => {
return request({
url: '/TransactionRecord/GetByDate',
method: 'get',
params: { date }
})
}
// 注意分类相关的API已迁移到 transactionCategory.js
// 请使用 getCategoryTree 等新接口