feat: Refactor transaction handling and add new features

- Updated ReasonGroupList.vue to modify classify button behavior for adding new classifications.
- Refactored TransactionDetail.vue to integrate PopupContainer and enhance transaction detail display.
- Improved TransactionDetailDialog.vue with updated classify button functionality.
- Simplified BalanceView.vue by removing manual entry button.
- Enhanced PeriodicRecord.vue to update classify button interactions.
- Removed unused add transaction dialog from TransactionsRecord.vue.
- Added new API endpoints in TransactionRecordController for parsing transactions and handling offsets.
- Introduced BillForm.vue and ManualBillAdd.vue for streamlined bill entry.
- Implemented OneLineBillAdd.vue for intelligent transaction parsing.
- Created GlobalAddBill.vue for a unified bill addition interface.
This commit is contained in:
2026-01-01 14:43:43 +08:00
parent 8dfe7f1688
commit e00b5cffb7
18 changed files with 977 additions and 384 deletions

View File

@@ -0,0 +1,126 @@
<template>
<div>
<div class="input-section" v-if="!parseResult" style="margin: 12px 12px 0 16px;">
<van-field
v-model="text"
type="textarea"
rows="4"
placeholder="例如1月3日 晚餐 45.5 美团"
class="bill-input"
:disabled="parsing || saving"
/>
<div class="actions">
<van-button
type="primary"
round
block
@click="handleParse"
:loading="parsing"
:disabled="!text.trim()"
>
智能解析
</van-button>
</div>
</div>
<div v-if="parseResult" class="result-section">
<BillForm
:initial-data="parseResult"
:loading="saving"
submit-text="确认保存"
@submit="handleSave"
>
<template #actions>
<van-button
plain
round
block
@click="parseResult = null"
class="mt-2"
>
重新输入
</van-button>
</template>
</BillForm>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { showToast } from 'vant'
import BillForm from './BillForm.vue'
import { createTransaction, parseOneLine } from '@/api/transactionRecord'
const emit = defineEmits(['success'])
const text = ref('')
const parsing = ref(false)
const saving = ref(false)
const parseResult = ref(null)
const handleParse = async () => {
if (!text.value.trim()) return
parsing.value = true
parseResult.value = null
try {
const res = await parseOneLine(text.value)
if(!res.success){
throw new Error(res.message || '解析失败')
}
parseResult.value = res.data
} catch (err) {
console.error(err)
showToast('解析失败:' + err.message)
} finally {
parsing.value = false
}
}
const handleSave = async (payload) => {
saving.value = true
try {
const res = await createTransaction(payload)
if (!res.success) {
throw new Error(res.message || '保存失败')
}
showToast('保存成功')
text.value = ''
parseResult.value = null
emit('success')
} catch (err) {
console.error(err)
showToast('保存失败:' + err.message)
} finally {
saving.value = false
}
}
</script>
<style scoped>
.bill-input {
background-color: var(--van-background-2);
border-radius: 8px;
margin-bottom: 16px;
}
.actions {
margin-top: 16px;
}
.mt-2 {
margin-top: 8px;
}
.preview-card {
margin-bottom: 16px;
border-radius: 8px;
overflow: hidden;
border: 1px solid #ebedf0;
}
</style>