2025-12-25 11:20:56 +08:00
|
|
|
|
<template>
|
2025-12-26 17:29:17 +08:00
|
|
|
|
<div class="page-container">
|
2025-12-25 11:20:56 +08:00
|
|
|
|
<!-- 顶部导航栏 -->
|
2025-12-25 17:41:36 +08:00
|
|
|
|
<van-nav-bar title="交易记录" placeholder>
|
2025-12-25 11:20:56 +08:00
|
|
|
|
<template #right>
|
|
|
|
|
|
<van-button type="primary" size="small" @click="openAddDialog">
|
|
|
|
|
|
手动录账
|
|
|
|
|
|
</van-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</van-nav-bar>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 下拉刷新区域 -->
|
|
|
|
|
|
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" class="refresh-wrapper">
|
|
|
|
|
|
<!-- 加载提示 -->
|
|
|
|
|
|
<van-loading v-if="loading && !(transactionList && transactionList.length)" vertical style="padding: 50px 0">
|
|
|
|
|
|
加载中...
|
|
|
|
|
|
</van-loading>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 交易记录列表 -->
|
|
|
|
|
|
<TransactionList
|
|
|
|
|
|
:transactions="transactionList"
|
|
|
|
|
|
:loading="loading"
|
|
|
|
|
|
:finished="finished"
|
|
|
|
|
|
@load="onLoad"
|
|
|
|
|
|
@click="viewDetail"
|
|
|
|
|
|
@delete="handleDelete"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</van-pull-refresh>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 详情/编辑弹出层 -->
|
|
|
|
|
|
<TransactionDetail
|
|
|
|
|
|
v-model:show="detailVisible"
|
|
|
|
|
|
:transaction="currentTransaction"
|
|
|
|
|
|
@save="onDetailSave"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 新增交易记录弹出层 -->
|
|
|
|
|
|
<van-popup
|
|
|
|
|
|
v-model:show="addDialogVisible"
|
|
|
|
|
|
position="bottom"
|
|
|
|
|
|
:style="{ height: '85%' }"
|
|
|
|
|
|
round
|
|
|
|
|
|
closeable
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="transaction-detail">
|
|
|
|
|
|
<div class="detail-header" style="padding-top: 10px; padding-left: 10px;">
|
|
|
|
|
|
<h3>手动录账单</h3>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<van-form @submit="onAddSubmit">
|
|
|
|
|
|
<van-cell-group inset title="基本信息">
|
|
|
|
|
|
<van-field
|
|
|
|
|
|
v-model="addForm.occurredAt"
|
|
|
|
|
|
is-link
|
|
|
|
|
|
readonly
|
|
|
|
|
|
name="occurredAt"
|
|
|
|
|
|
label="交易时间"
|
|
|
|
|
|
placeholder="请选择交易时间"
|
|
|
|
|
|
@click="showDateTimePicker = true"
|
|
|
|
|
|
:rules="[{ required: true, message: '请选择交易时间' }]"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</van-cell-group>
|
|
|
|
|
|
|
|
|
|
|
|
<van-cell-group inset title="交易明细">
|
|
|
|
|
|
<van-field
|
|
|
|
|
|
v-model="addForm.reason"
|
|
|
|
|
|
name="reason"
|
|
|
|
|
|
label="交易摘要"
|
|
|
|
|
|
placeholder="请输入交易摘要"
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
rows="2"
|
|
|
|
|
|
autosize
|
|
|
|
|
|
maxlength="200"
|
|
|
|
|
|
show-word-limit
|
|
|
|
|
|
/>
|
|
|
|
|
|
<van-field
|
|
|
|
|
|
v-model="addForm.amount"
|
|
|
|
|
|
name="amount"
|
|
|
|
|
|
label="交易金额"
|
|
|
|
|
|
placeholder="请输入交易金额"
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
:rules="[{ required: true, message: '请输入交易金额' }]"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<van-field
|
|
|
|
|
|
v-model="addForm.typeText"
|
|
|
|
|
|
is-link
|
|
|
|
|
|
readonly
|
|
|
|
|
|
name="type"
|
|
|
|
|
|
label="交易类型"
|
|
|
|
|
|
placeholder="请选择交易类型"
|
|
|
|
|
|
@click="showAddTypePicker = true"
|
|
|
|
|
|
:rules="[{ required: true, message: '请选择交易类型' }]"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<van-field
|
|
|
|
|
|
v-model="addForm.classify"
|
|
|
|
|
|
is-link
|
|
|
|
|
|
readonly
|
|
|
|
|
|
name="classify"
|
|
|
|
|
|
label="交易分类"
|
|
|
|
|
|
placeholder="请选择或输入交易分类"
|
|
|
|
|
|
@click="showAddClassifyPicker = true"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</van-cell-group>
|
|
|
|
|
|
|
|
|
|
|
|
<div style="margin: 16px;">
|
|
|
|
|
|
<van-button round block type="primary" native-type="submit" :loading="addSubmitting">
|
|
|
|
|
|
确认添加
|
|
|
|
|
|
</van-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</van-form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</van-popup>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 新增交易 - 日期时间选择器 -->
|
|
|
|
|
|
<van-popup v-model:show="showDateTimePicker" position="bottom" round>
|
|
|
|
|
|
<van-date-picker
|
|
|
|
|
|
v-model="dateTimeValue"
|
|
|
|
|
|
title="选择日期时间"
|
|
|
|
|
|
:min-date="new Date(2020, 0, 1)"
|
|
|
|
|
|
:max-date="new Date()"
|
|
|
|
|
|
@confirm="onDateTimeConfirm"
|
|
|
|
|
|
@cancel="showDateTimePicker = false"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</van-popup>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 新增交易 - 交易类型选择器 -->
|
|
|
|
|
|
<van-popup v-model:show="showAddTypePicker" position="bottom" round>
|
|
|
|
|
|
<van-picker
|
|
|
|
|
|
show-toolbar
|
|
|
|
|
|
:columns="typeColumns"
|
|
|
|
|
|
@confirm="onAddTypeConfirm"
|
|
|
|
|
|
@cancel="showAddTypePicker = false"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</van-popup>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 新增交易 - 交易分类选择器 -->
|
|
|
|
|
|
<van-popup v-model:show="showAddClassifyPicker" position="bottom" round>
|
|
|
|
|
|
<van-picker
|
|
|
|
|
|
ref="addClassifyPickerRef"
|
|
|
|
|
|
:columns="classifyColumns"
|
|
|
|
|
|
@confirm="onAddClassifyConfirm"
|
|
|
|
|
|
@cancel="showAddClassifyPicker = false"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #toolbar>
|
|
|
|
|
|
<div class="picker-toolbar">
|
|
|
|
|
|
<van-button class="toolbar-cancel" size="small" @click="clearAddClassify">清空</van-button>
|
|
|
|
|
|
<van-button class="toolbar-add" size="small" type="primary" @click="showAddClassify = true">新增</van-button>
|
|
|
|
|
|
<van-button class="toolbar-confirm" size="small" type="primary" @click="confirmAddClassify">确认</van-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</van-picker>
|
|
|
|
|
|
</van-popup>
|
|
|
|
|
|
|
2025-12-26 15:21:31 +08:00
|
|
|
|
<!-- 新增分类对话框 -->
|
|
|
|
|
|
<van-dialog
|
|
|
|
|
|
v-model:show="showAddClassify"
|
|
|
|
|
|
title="新增交易分类"
|
|
|
|
|
|
show-cancel-button
|
|
|
|
|
|
@confirm="addNewClassify"
|
|
|
|
|
|
>
|
|
|
|
|
|
<van-field v-model="newClassify" placeholder="请输入新的交易分类" />
|
|
|
|
|
|
</van-dialog>
|
2025-12-25 11:20:56 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 底部浮动搜索框 -->
|
|
|
|
|
|
<div class="floating-search">
|
|
|
|
|
|
<van-search
|
|
|
|
|
|
v-model="searchKeyword"
|
2025-12-26 15:21:31 +08:00
|
|
|
|
placeholder="搜索交易摘要、来源、卡号、分类"
|
2025-12-25 11:20:56 +08:00
|
|
|
|
@update:model-value="onSearchChange"
|
|
|
|
|
|
@clear="onSearchClear"
|
|
|
|
|
|
shape="round"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
|
|
|
|
import { showToast, showConfirmDialog } from 'vant'
|
|
|
|
|
|
import {
|
|
|
|
|
|
getTransactionList,
|
|
|
|
|
|
getTransactionDetail,
|
|
|
|
|
|
createTransaction,
|
|
|
|
|
|
deleteTransaction
|
|
|
|
|
|
} from '@/api/transactionRecord'
|
2025-12-26 15:21:31 +08:00
|
|
|
|
import { getCategoryList, createCategory } from '@/api/transactionCategory'
|
2025-12-25 11:20:56 +08:00
|
|
|
|
import TransactionList from '@/components/TransactionList.vue'
|
|
|
|
|
|
import TransactionDetail from '@/components/TransactionDetail.vue'
|
|
|
|
|
|
|
|
|
|
|
|
const transactionList = ref([])
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const refreshing = ref(false)
|
|
|
|
|
|
const finished = ref(false)
|
|
|
|
|
|
const lastId = ref(null)
|
|
|
|
|
|
const lastTime = ref(null)
|
|
|
|
|
|
const total = ref(0)
|
|
|
|
|
|
const detailVisible = ref(false)
|
|
|
|
|
|
const currentTransaction = ref(null)
|
|
|
|
|
|
|
|
|
|
|
|
// 搜索相关
|
|
|
|
|
|
const searchKeyword = ref('')
|
|
|
|
|
|
let searchTimer = null
|
|
|
|
|
|
|
|
|
|
|
|
// 新增交易弹窗相关
|
|
|
|
|
|
const addDialogVisible = ref(false)
|
|
|
|
|
|
const addSubmitting = ref(false)
|
|
|
|
|
|
const showDateTimePicker = ref(false)
|
|
|
|
|
|
const dateTimeValue = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()])
|
|
|
|
|
|
const showAddTypePicker = ref(false)
|
|
|
|
|
|
const showAddClassifyPicker = ref(false)
|
|
|
|
|
|
const addClassifyPickerRef = ref(null)
|
2025-12-26 15:21:31 +08:00
|
|
|
|
const showAddClassify = ref(false)
|
|
|
|
|
|
const newClassify = ref('')
|
2025-12-25 11:20:56 +08:00
|
|
|
|
|
|
|
|
|
|
// 交易类型
|
|
|
|
|
|
const typeColumns = [
|
|
|
|
|
|
{ text: '支出', value: 0 },
|
|
|
|
|
|
{ text: '收入', value: 1 },
|
|
|
|
|
|
{ text: '不计入收支', value: 2 }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 分类相关
|
|
|
|
|
|
const classifyColumns = ref([])
|
|
|
|
|
|
|
|
|
|
|
|
// 新增表单
|
|
|
|
|
|
const addForm = reactive({
|
|
|
|
|
|
occurredAt: '',
|
|
|
|
|
|
reason: '',
|
|
|
|
|
|
amount: '',
|
|
|
|
|
|
type: 0,
|
|
|
|
|
|
typeText: '',
|
2025-12-26 15:21:31 +08:00
|
|
|
|
classify: ''
|
2025-12-25 11:20:56 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2025-12-26 15:21:31 +08:00
|
|
|
|
// 加载分类列表
|
2025-12-25 11:20:56 +08:00
|
|
|
|
const loadClassifyList = async (type = null) => {
|
|
|
|
|
|
try {
|
2025-12-26 15:21:31 +08:00
|
|
|
|
const response = await getCategoryList(type)
|
2025-12-25 11:20:56 +08:00
|
|
|
|
if (response.success) {
|
|
|
|
|
|
classifyColumns.value = (response.data || []).map(item => ({
|
|
|
|
|
|
text: item.name,
|
|
|
|
|
|
value: item.name,
|
2025-12-26 15:21:31 +08:00
|
|
|
|
id: item.id
|
2025-12-25 11:20:56 +08:00
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载分类列表出错:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载数据
|
|
|
|
|
|
const loadData = async (isRefresh = false) => {
|
|
|
|
|
|
if (isRefresh) {
|
|
|
|
|
|
lastId.value = null
|
|
|
|
|
|
lastTime.value = null
|
|
|
|
|
|
transactionList.value = []
|
|
|
|
|
|
finished.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const params = {}
|
|
|
|
|
|
if (lastTime.value && lastId.value) {
|
|
|
|
|
|
params.lastOccurredAt = lastTime.value
|
|
|
|
|
|
params.lastId = lastId.value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加搜索关键词
|
|
|
|
|
|
if (searchKeyword.value) {
|
|
|
|
|
|
params.searchKeyword = searchKeyword.value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const response = await getTransactionList(params)
|
|
|
|
|
|
|
|
|
|
|
|
if (response.success) {
|
|
|
|
|
|
const newList = response.data || []
|
|
|
|
|
|
total.value = response.total || 0
|
|
|
|
|
|
const newLastId = response.lastId || 0
|
|
|
|
|
|
const newLastTime = response.lastTime
|
|
|
|
|
|
|
|
|
|
|
|
if (isRefresh) {
|
|
|
|
|
|
transactionList.value = newList
|
|
|
|
|
|
} else {
|
|
|
|
|
|
transactionList.value = [...(transactionList.value || []), ...newList]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (newLastId > 0 && newLastTime) {
|
|
|
|
|
|
lastId.value = newLastId
|
|
|
|
|
|
lastTime.value = newLastTime
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (newList.length === 0 || newList.length < 20) {
|
|
|
|
|
|
finished.value = true
|
|
|
|
|
|
} else {
|
|
|
|
|
|
finished.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showToast(response.message || '加载数据失败')
|
|
|
|
|
|
finished.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载数据出错:', error)
|
|
|
|
|
|
showToast('加载数据出错: ' + (error.message || '未知错误'))
|
|
|
|
|
|
finished.value = true
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
refreshing.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 下拉刷新
|
|
|
|
|
|
const onRefresh = () => {
|
|
|
|
|
|
finished.value = false
|
|
|
|
|
|
lastId.value = null
|
|
|
|
|
|
transactionList.value = []
|
|
|
|
|
|
loadData(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 搜索相关方法
|
|
|
|
|
|
const onSearchChange = () => {
|
|
|
|
|
|
// 防抖处理,用户停止输入500ms后自动搜索
|
|
|
|
|
|
if (searchTimer) {
|
|
|
|
|
|
clearTimeout(searchTimer)
|
|
|
|
|
|
}
|
|
|
|
|
|
searchTimer = setTimeout(() => {
|
|
|
|
|
|
onSearch()
|
|
|
|
|
|
}, 500)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const onSearch = () => {
|
|
|
|
|
|
// 重置分页状态并刷新数据
|
|
|
|
|
|
lastId.value = null
|
|
|
|
|
|
lastTime.value = null
|
|
|
|
|
|
transactionList.value = []
|
|
|
|
|
|
finished.value = false
|
|
|
|
|
|
loadData(true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const onSearchClear = () => {
|
|
|
|
|
|
searchKeyword.value = ''
|
|
|
|
|
|
onSearch()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载更多
|
|
|
|
|
|
const onLoad = () => {
|
|
|
|
|
|
loadData(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查看详情
|
|
|
|
|
|
const viewDetail = async (transaction) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await getTransactionDetail(transaction.id)
|
|
|
|
|
|
if (response.success) {
|
|
|
|
|
|
currentTransaction.value = response.data
|
|
|
|
|
|
detailVisible.value = true
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showToast(response.message || '获取详情失败')
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取详情出错:', error)
|
|
|
|
|
|
showToast('获取详情失败')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 详情保存后的回调
|
|
|
|
|
|
const onDetailSave = async () => {
|
|
|
|
|
|
loadData(true)
|
|
|
|
|
|
// 重新加载分类列表
|
|
|
|
|
|
await loadClassifyList()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除
|
|
|
|
|
|
const handleDelete = async (transaction) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await showConfirmDialog({
|
|
|
|
|
|
title: '提示',
|
|
|
|
|
|
message: '确定要删除这条交易记录吗?',
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const response = await deleteTransaction(transaction.id)
|
|
|
|
|
|
if (response.success) {
|
|
|
|
|
|
showToast('删除成功')
|
|
|
|
|
|
loadData(true)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showToast(response.message || '删除失败')
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
if (error !== 'cancel') {
|
|
|
|
|
|
console.error('删除出错:', error)
|
|
|
|
|
|
showToast('删除失败')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 打开新增弹窗
|
|
|
|
|
|
const openAddDialog = () => {
|
|
|
|
|
|
// 重置表单
|
|
|
|
|
|
addForm.occurredAt = ''
|
|
|
|
|
|
addForm.reason = ''
|
|
|
|
|
|
addForm.amount = ''
|
|
|
|
|
|
addForm.type = 0
|
|
|
|
|
|
addForm.typeText = ''
|
|
|
|
|
|
addForm.classify = ''
|
|
|
|
|
|
|
|
|
|
|
|
// 设置默认日期时间为当前时间
|
|
|
|
|
|
const now = new Date()
|
|
|
|
|
|
dateTimeValue.value = [now.getFullYear(), now.getMonth() + 1, now.getDate()]
|
|
|
|
|
|
addForm.occurredAt = formatDateForSubmit(now)
|
|
|
|
|
|
|
|
|
|
|
|
addDialogVisible.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化日期用于提交
|
|
|
|
|
|
const formatDateForSubmit = (date) => {
|
|
|
|
|
|
const year = date.getFullYear()
|
|
|
|
|
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
|
|
|
const day = String(date.getDate()).padStart(2, '0')
|
|
|
|
|
|
const hours = String(date.getHours()).padStart(2, '0')
|
|
|
|
|
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
|
|
|
|
|
const seconds = String(date.getSeconds()).padStart(2, '0')
|
|
|
|
|
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 日期时间选择确认
|
|
|
|
|
|
const onDateTimeConfirm = ({ selectedValues }) => {
|
|
|
|
|
|
const date = new Date(selectedValues[0], selectedValues[1] - 1, selectedValues[2])
|
|
|
|
|
|
addForm.occurredAt = formatDateForSubmit(date)
|
|
|
|
|
|
showDateTimePicker.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 新增交易 - 交易类型选择确认
|
|
|
|
|
|
const onAddTypeConfirm = ({ selectedValues, selectedOptions }) => {
|
|
|
|
|
|
addForm.type = selectedValues[0]
|
|
|
|
|
|
addForm.typeText = selectedOptions[0].text
|
|
|
|
|
|
showAddTypePicker.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 新增交易 - 交易分类选择确认
|
2025-12-26 15:21:31 +08:00
|
|
|
|
const onAddClassifyConfirm = ({ selectedOptions }) => {
|
2025-12-25 11:20:56 +08:00
|
|
|
|
if (selectedOptions && selectedOptions[0]) {
|
|
|
|
|
|
addForm.classify = selectedOptions[0].text
|
|
|
|
|
|
}
|
|
|
|
|
|
showAddClassifyPicker.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 新增交易 - 清空分类
|
|
|
|
|
|
const clearAddClassify = () => {
|
|
|
|
|
|
addForm.classify = ''
|
|
|
|
|
|
showAddClassifyPicker.value = false
|
|
|
|
|
|
showToast('已清空分类')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 新增交易 - 确认分类(从 picker 中获取选中值)
|
|
|
|
|
|
const confirmAddClassify = () => {
|
|
|
|
|
|
if (addClassifyPickerRef.value) {
|
|
|
|
|
|
const selectedValues = addClassifyPickerRef.value.getSelectedOptions()
|
|
|
|
|
|
if (selectedValues && selectedValues[0]) {
|
|
|
|
|
|
addForm.classify = selectedValues[0].text
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
showAddClassifyPicker.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 15:21:31 +08:00
|
|
|
|
// 新增分类
|
|
|
|
|
|
const addNewClassify = async () => {
|
|
|
|
|
|
if (!newClassify.value.trim()) {
|
|
|
|
|
|
showToast('请输入分类名称')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await createCategory({
|
|
|
|
|
|
name: newClassify.value.trim(),
|
|
|
|
|
|
type: addForm.type
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (response.success) {
|
|
|
|
|
|
showToast('新增分类成功')
|
|
|
|
|
|
newClassify.value = ''
|
|
|
|
|
|
// 重新加载分类列表
|
|
|
|
|
|
await loadClassifyList(addForm.type)
|
|
|
|
|
|
// 设置为新增的分类
|
|
|
|
|
|
addForm.classify = response.data.name
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showToast(response.message || '新增分类失败')
|
2025-12-25 11:20:56 +08:00
|
|
|
|
}
|
2025-12-26 15:21:31 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('新增分类失败:', error)
|
|
|
|
|
|
showToast('新增分类失败')
|
2025-12-25 11:20:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 提交新增交易
|
|
|
|
|
|
const onAddSubmit = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
addSubmitting.value = true
|
|
|
|
|
|
|
|
|
|
|
|
const data = {
|
|
|
|
|
|
occurredAt: addForm.occurredAt,
|
|
|
|
|
|
reason: addForm.reason,
|
|
|
|
|
|
amount: parseFloat(addForm.amount),
|
|
|
|
|
|
type: addForm.type,
|
2025-12-26 15:21:31 +08:00
|
|
|
|
classify: addForm.classify || null
|
2025-12-25 11:20:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const response = await createTransaction(data)
|
|
|
|
|
|
if (response.success) {
|
|
|
|
|
|
showToast('添加成功')
|
|
|
|
|
|
addDialogVisible.value = false
|
|
|
|
|
|
loadData(true)
|
|
|
|
|
|
// 重新加载分类列表
|
|
|
|
|
|
await loadClassifyList()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showToast(response.message || '添加失败')
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('添加出错:', error)
|
|
|
|
|
|
showToast('添加失败: ' + (error.message || '未知错误'))
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
addSubmitting.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
await loadClassifyList()
|
|
|
|
|
|
// 不需要手动调用 loadData,van-list 会自动触发 onLoad
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
|
|
|
|
|
|
.floating-search {
|
|
|
|
|
|
position: fixed;
|
2025-12-25 17:28:06 +08:00
|
|
|
|
bottom: 90px;
|
2025-12-25 11:20:56 +08:00
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
z-index: 999;
|
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.floating-search :deep(.van-search) {
|
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
|
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.picker-toolbar {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
|
border-bottom: 1px solid #ebedf0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar-cancel {
|
|
|
|
|
|
margin-right: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar-confirm {
|
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|