first commot
This commit is contained in:
521
Web/src/components/TransactionDetail.vue
Normal file
521
Web/src/components/TransactionDetail.vue
Normal file
@@ -0,0 +1,521 @@
|
||||
<template>
|
||||
<van-popup
|
||||
v-model:show="visible"
|
||||
position="bottom"
|
||||
:style="{ height: '85%' }"
|
||||
round
|
||||
closeable
|
||||
@update:show="handleVisibleChange"
|
||||
>
|
||||
<div class="transaction-detail" v-if="transaction">
|
||||
<div class="detail-header" style="margin-top: 10px;margin-left: 10px;">
|
||||
<h3>交易详情</h3>
|
||||
</div>
|
||||
|
||||
<van-form @submit="onSubmit">
|
||||
<van-cell-group inset>
|
||||
<van-cell title="卡号" :value="transaction.card" />
|
||||
<van-cell title="交易时间" :value="formatDate(transaction.occurredAt)" />
|
||||
<van-cell title="记录时间" :value="formatDate(transaction.createTime)" />
|
||||
</van-cell-group>
|
||||
|
||||
<van-cell-group inset title="交易明细">
|
||||
<van-field
|
||||
v-model="editForm.reason"
|
||||
name="reason"
|
||||
label="交易摘要"
|
||||
placeholder="请输入交易摘要"
|
||||
type="textarea"
|
||||
rows="2"
|
||||
autosize
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
<van-field
|
||||
v-model="editForm.amount"
|
||||
name="amount"
|
||||
label="交易金额"
|
||||
placeholder="请输入交易金额"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入交易金额' }]"
|
||||
/>
|
||||
<van-field
|
||||
v-model="editForm.balance"
|
||||
name="balance"
|
||||
label="交易后余额"
|
||||
placeholder="请输入交易后余额"
|
||||
type="number"
|
||||
:rules="[{ required: true, message: '请输入交易后余额' }]"
|
||||
/>
|
||||
<van-field
|
||||
v-model="editForm.typeText"
|
||||
is-link
|
||||
readonly
|
||||
name="type"
|
||||
label="交易类型"
|
||||
placeholder="请选择交易类型"
|
||||
@click="showTypePicker = true"
|
||||
:rules="[{ required: true, message: '请选择交易类型' }]"
|
||||
/>
|
||||
<van-field
|
||||
v-model="editForm.classify"
|
||||
is-link
|
||||
readonly
|
||||
name="classify"
|
||||
label="交易分类"
|
||||
placeholder="请选择或输入交易分类"
|
||||
@click="openClassifyPicker"
|
||||
/>
|
||||
<van-field
|
||||
v-model="editForm.subClassify"
|
||||
is-link
|
||||
readonly
|
||||
name="subClassify"
|
||||
label="交易子分类"
|
||||
placeholder="请选择或输入交易子分类"
|
||||
@click="showSubClassifyPicker = true"
|
||||
/>
|
||||
</van-cell-group>
|
||||
|
||||
<div style="margin: 16px;">
|
||||
<van-button round block type="primary" native-type="submit" :loading="submitting">
|
||||
保存修改
|
||||
</van-button>
|
||||
</div>
|
||||
</van-form>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<!-- 交易类型选择器 -->
|
||||
<van-popup v-model:show="showTypePicker" position="bottom" round>
|
||||
<van-picker
|
||||
show-toolbar
|
||||
:columns="typeColumns"
|
||||
@confirm="onTypeConfirm"
|
||||
@cancel="showTypePicker = false"
|
||||
/>
|
||||
</van-popup>
|
||||
|
||||
<!-- 交易分类选择器 -->
|
||||
<van-popup v-model:show="showClassifyPicker" position="bottom" round>
|
||||
<van-picker
|
||||
ref="classifyPickerRef"
|
||||
:columns="classifyColumns"
|
||||
@confirm="onClassifyConfirm"
|
||||
@cancel="showClassifyPicker = false"
|
||||
>
|
||||
<template #toolbar>
|
||||
<div class="picker-toolbar">
|
||||
<van-button class="toolbar-cancel" size="small" @click="clearClassify">清空</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="confirmClassify">确认</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</van-picker>
|
||||
</van-popup>
|
||||
|
||||
<!-- 交易子分类选择器 -->
|
||||
<van-popup v-model:show="showSubClassifyPicker" position="bottom" round>
|
||||
<van-picker
|
||||
ref="subClassifyPickerRef"
|
||||
:columns="subClassifyColumns"
|
||||
@confirm="onSubClassifyConfirm"
|
||||
@cancel="showSubClassifyPicker = false"
|
||||
>
|
||||
<template #toolbar>
|
||||
<div class="picker-toolbar">
|
||||
<van-button class="toolbar-cancel" size="small" @click="clearSubClassify">清空</van-button>
|
||||
<van-button class="toolbar-add" size="small" type="primary" @click="showAddSubClassify = true">新增</van-button>
|
||||
<van-button class="toolbar-confirm" size="small" type="primary" @click="confirmSubClassify">确认</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</van-picker>
|
||||
</van-popup>
|
||||
|
||||
<!-- 新增分类对话框 -->
|
||||
<van-dialog
|
||||
v-model:show="showAddClassify"
|
||||
title="新增交易分类"
|
||||
show-cancel-button
|
||||
@confirm="addNewClassify"
|
||||
>
|
||||
<van-field v-model="newClassify" placeholder="请输入新的交易分类" />
|
||||
</van-dialog>
|
||||
|
||||
<!-- 新增子分类对话框 -->
|
||||
<van-dialog
|
||||
v-model:show="showAddSubClassify"
|
||||
title="新增交易子分类"
|
||||
show-cancel-button
|
||||
@confirm="addNewSubClassify"
|
||||
>
|
||||
<van-field v-model="newSubClassify" placeholder="请输入新的交易子分类" />
|
||||
</van-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, defineProps, defineEmits } from 'vue'
|
||||
import { showToast } from 'vant'
|
||||
import { updateTransaction } from '@/api/transactionRecord'
|
||||
import { getCategoryTree, createCategory } from '@/api/transactionCategory'
|
||||
|
||||
const props = defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
transaction: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:show', 'save'])
|
||||
|
||||
const visible = ref(false)
|
||||
const submitting = ref(false)
|
||||
|
||||
// 交易类型
|
||||
const typeColumns = [
|
||||
{ text: '支出', value: 0 },
|
||||
{ text: '收入', value: 1 },
|
||||
{ text: '不计入收支', value: 2 }
|
||||
]
|
||||
|
||||
// 分类相关
|
||||
const classifyColumns = ref([])
|
||||
const subClassifyColumns = ref([])
|
||||
const showTypePicker = ref(false)
|
||||
const showClassifyPicker = ref(false)
|
||||
const showSubClassifyPicker = ref(false)
|
||||
const showAddClassify = ref(false)
|
||||
const showAddSubClassify = ref(false)
|
||||
const newClassify = ref('')
|
||||
const newSubClassify = ref('')
|
||||
const classifyPickerRef = ref(null)
|
||||
const subClassifyPickerRef = ref(null)
|
||||
|
||||
// 编辑表单
|
||||
const editForm = reactive({
|
||||
id: 0,
|
||||
reason: '',
|
||||
amount: '',
|
||||
balance: '',
|
||||
type: 0,
|
||||
typeText: '',
|
||||
classify: '',
|
||||
subClassify: ''
|
||||
})
|
||||
|
||||
// 监听props变化
|
||||
watch(() => props.show, (newVal) => {
|
||||
visible.value = newVal
|
||||
})
|
||||
|
||||
watch(() => props.transaction, (newVal) => {
|
||||
if (newVal) {
|
||||
// 填充编辑表单
|
||||
editForm.id = newVal.id
|
||||
editForm.reason = newVal.reason || ''
|
||||
editForm.amount = String(newVal.amount)
|
||||
editForm.balance = String(newVal.balance)
|
||||
editForm.type = newVal.type
|
||||
editForm.typeText = getTypeName(newVal.type)
|
||||
editForm.classify = newVal.classify || ''
|
||||
editForm.subClassify = newVal.subClassify || ''
|
||||
|
||||
// 根据交易类型加载分类
|
||||
loadClassifyList(newVal.type)
|
||||
}
|
||||
})
|
||||
|
||||
watch(visible, (newVal) => {
|
||||
emit('update:show', newVal)
|
||||
})
|
||||
|
||||
// 监听交易类型变化,重新加载分类
|
||||
watch(() => editForm.type, (newVal) => {
|
||||
// 清空已选的分类和子分类
|
||||
editForm.classify = ''
|
||||
editForm.subClassify = ''
|
||||
// 重新加载对应类型的分类列表
|
||||
loadClassifyList(newVal)
|
||||
})
|
||||
|
||||
const handleVisibleChange = (newVal) => {
|
||||
emit('update:show', newVal)
|
||||
}
|
||||
|
||||
// 加载分类列表(从分类树中提取)
|
||||
const loadClassifyList = async (type = null) => {
|
||||
try {
|
||||
const response = await getCategoryTree(type)
|
||||
if (response.success) {
|
||||
// 从树形结构中提取分类名称(Level 2)
|
||||
classifyColumns.value = (response.data || []).map(item => ({
|
||||
text: item.name,
|
||||
value: item.name,
|
||||
id: item.id,
|
||||
children: item.children || []
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载分类列表出错:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载子分类列表(根据选中的分类)
|
||||
const loadSubClassifyList = async (classifyName) => {
|
||||
try {
|
||||
// 从已加载的分类树中查找对应的子分类
|
||||
const classifyItem = classifyColumns.value.find(item => item.value === classifyName)
|
||||
if (classifyItem && classifyItem.children) {
|
||||
subClassifyColumns.value = classifyItem.children.map(child => ({
|
||||
text: child.name,
|
||||
value: child.name,
|
||||
id: child.id
|
||||
}))
|
||||
} else {
|
||||
subClassifyColumns.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载子分类列表出错:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 提交编辑
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
submitting.value = true
|
||||
|
||||
const data = {
|
||||
id: editForm.id,
|
||||
reason: editForm.reason,
|
||||
amount: parseFloat(editForm.amount),
|
||||
balance: parseFloat(editForm.balance),
|
||||
type: editForm.type,
|
||||
classify: editForm.classify,
|
||||
subClassify: editForm.subClassify
|
||||
}
|
||||
|
||||
const response = await updateTransaction(data)
|
||||
if (response.success) {
|
||||
showToast('保存成功')
|
||||
visible.value = false
|
||||
emit('save')
|
||||
// 重新加载分类列表
|
||||
await loadClassifyList(editForm.type)
|
||||
} else {
|
||||
showToast(response.message || '保存失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存出错:', error)
|
||||
showToast('保存失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 打开分类选择器
|
||||
const openClassifyPicker = async () => {
|
||||
// 先根据当前交易类型加载分类
|
||||
await loadClassifyList(editForm.type)
|
||||
showClassifyPicker.value = true
|
||||
}
|
||||
|
||||
// 交易类型选择确认
|
||||
const onTypeConfirm = ({ selectedValues, selectedOptions }) => {
|
||||
editForm.type = selectedValues[0]
|
||||
editForm.typeText = selectedOptions[0].text
|
||||
showTypePicker.value = false
|
||||
}
|
||||
|
||||
// 交易分类选择确认
|
||||
const onClassifyConfirm = async ({ selectedOptions }) => {
|
||||
if (selectedOptions && selectedOptions[0]) {
|
||||
editForm.classify = selectedOptions[0].text
|
||||
// 加载对应的子分类
|
||||
await loadSubClassifyList(selectedOptions[0].value)
|
||||
}
|
||||
showClassifyPicker.value = false
|
||||
}
|
||||
|
||||
// 交易子分类选择确认
|
||||
const onSubClassifyConfirm = ({ selectedOptions }) => {
|
||||
if (selectedOptions && selectedOptions[0]) {
|
||||
editForm.subClassify = selectedOptions[0].text
|
||||
}
|
||||
showSubClassifyPicker.value = false
|
||||
}
|
||||
|
||||
// 新增分类
|
||||
const addNewClassify = async () => {
|
||||
if (!newClassify.value.trim()) {
|
||||
showToast('请输入分类名称')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const categoryName = newClassify.value.trim()
|
||||
|
||||
// 调用API创建分类
|
||||
const response = await createCategory({
|
||||
name: categoryName,
|
||||
parentId: 0,
|
||||
type: editForm.type,
|
||||
level: 2,
|
||||
sortOrder: 0
|
||||
})
|
||||
|
||||
if (response.success) {
|
||||
showToast('分类创建成功')
|
||||
// 重新加载分类列表
|
||||
await loadClassifyList(editForm.type)
|
||||
editForm.classify = categoryName
|
||||
} else {
|
||||
showToast(response.message || '创建分类失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('创建分类出错:', error)
|
||||
showToast('创建分类失败')
|
||||
} finally {
|
||||
newClassify.value = ''
|
||||
showAddClassify.value = false
|
||||
showClassifyPicker.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 新增子分类
|
||||
const addNewSubClassify = async () => {
|
||||
if (!newSubClassify.value.trim()) {
|
||||
showToast('请输入子分类名称')
|
||||
return
|
||||
}
|
||||
|
||||
if (!editForm.classify) {
|
||||
showToast('请先选择分类')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const subCategoryName = newSubClassify.value.trim()
|
||||
|
||||
// 找到父分类的ID
|
||||
const parentCategory = classifyColumns.value.find(c => c.value === editForm.classify)
|
||||
if (!parentCategory || !parentCategory.id) {
|
||||
showToast('未找到父分类信息')
|
||||
return
|
||||
}
|
||||
|
||||
// 调用API创建子分类
|
||||
const response = await createCategory({
|
||||
name: subCategoryName,
|
||||
parentId: parentCategory.id,
|
||||
type: editForm.type,
|
||||
level: 3,
|
||||
sortOrder: 0
|
||||
})
|
||||
|
||||
if (response.success) {
|
||||
showToast('子分类创建成功')
|
||||
// 重新加载子分类列表
|
||||
await loadSubClassifyList(editForm.classify)
|
||||
editForm.subClassify = subCategoryName
|
||||
} else {
|
||||
showToast(response.message || '创建子分类失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('创建子分类出错:', error)
|
||||
showToast('创建子分类失败')
|
||||
} finally {
|
||||
newSubClassify.value = ''
|
||||
showAddSubClassify.value = false
|
||||
showSubClassifyPicker.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 清空分类
|
||||
const clearClassify = () => {
|
||||
editForm.classify = ''
|
||||
showClassifyPicker.value = false
|
||||
showToast('已清空分类')
|
||||
}
|
||||
|
||||
// 清空子分类
|
||||
const clearSubClassify = () => {
|
||||
editForm.subClassify = ''
|
||||
showSubClassifyPicker.value = false
|
||||
showToast('已清空子分类')
|
||||
}
|
||||
|
||||
// 确认分类(从 picker 中获取选中值)
|
||||
const confirmClassify = () => {
|
||||
if (classifyPickerRef.value) {
|
||||
const selectedValues = classifyPickerRef.value.getSelectedOptions()
|
||||
if (selectedValues && selectedValues[0]) {
|
||||
editForm.classify = selectedValues[0].text
|
||||
}
|
||||
}
|
||||
showClassifyPicker.value = false
|
||||
}
|
||||
|
||||
// 确认子分类(从 picker 中获取选中值)
|
||||
const confirmSubClassify = () => {
|
||||
if (subClassifyPickerRef.value) {
|
||||
const selectedValues = subClassifyPickerRef.value.getSelectedOptions()
|
||||
if (selectedValues && selectedValues[0]) {
|
||||
editForm.subClassify = selectedValues[0].text
|
||||
}
|
||||
}
|
||||
showSubClassifyPicker.value = false
|
||||
}
|
||||
|
||||
// 获取交易类型名称
|
||||
const getTypeName = (type) => {
|
||||
const typeMap = {
|
||||
0: '支出',
|
||||
1: '收入',
|
||||
2: '不计入收支'
|
||||
}
|
||||
return typeMap[type] || '未知'
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString) return ''
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.transaction-detail {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.detail-header h3 {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.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>
|
||||
251
Web/src/components/TransactionList.vue
Normal file
251
Web/src/components/TransactionList.vue
Normal file
@@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<div class="transaction-list-container">
|
||||
<van-list
|
||||
:loading="loading"
|
||||
:finished="finished"
|
||||
finished-text="没有更多了"
|
||||
@load="onLoad"
|
||||
>
|
||||
<van-cell-group v-if="transactions && transactions.length" inset style="margin-top: 10px">
|
||||
<van-swipe-cell
|
||||
v-for="transaction in transactions"
|
||||
:key="transaction.id"
|
||||
>
|
||||
<div
|
||||
class="transaction-card"
|
||||
@click="handleClick(transaction)"
|
||||
>
|
||||
<div class="card-left">
|
||||
<div class="transaction-title">
|
||||
<span class="reason">{{ transaction.reason || '(无摘要)' }}</span>
|
||||
<van-tag
|
||||
:type="getTypeTagType(transaction.type)"
|
||||
size="medium"
|
||||
>
|
||||
{{ getTypeName(transaction.type) }}
|
||||
</van-tag>
|
||||
</div>
|
||||
<div class="transaction-info">
|
||||
<div>交易时间: {{ formatDate(transaction.occurredAt) }}</div>
|
||||
<div v-if="transaction.classify">分类: {{ transaction.classify }}
|
||||
<span v-if="transaction.subClassify">/ {{ transaction.subClassify }}</span>
|
||||
</div>
|
||||
<div v-if="transaction.card">
|
||||
卡号: {{ transaction.card }}
|
||||
</div>
|
||||
<div v-if="transaction.importFrom">
|
||||
来源: {{ transaction.importFrom }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-right">
|
||||
<div class="transaction-amount">
|
||||
<div :class="['amount', getAmountClass(transaction.type)]">
|
||||
{{ formatAmount(transaction.amount, transaction.type) }}
|
||||
</div>
|
||||
<div class="balance" v-if="transaction.balance && transaction.balance > 0">
|
||||
余额: {{ formatMoney(transaction.balance) }}
|
||||
</div>
|
||||
<div class="balance" v-if="transaction.refundAmount && transaction.refundAmount > 0">
|
||||
退款: {{ formatMoney(transaction.refundAmount) }}
|
||||
</div>
|
||||
</div>
|
||||
<van-icon name="arrow" size="16" color="#c8c9cc" />
|
||||
</div>
|
||||
</div>
|
||||
<template #right v-if="showDelete">
|
||||
<van-button
|
||||
square
|
||||
type="danger"
|
||||
text="删除"
|
||||
class="delete-button"
|
||||
@click="handleDeleteClick(transaction)"
|
||||
/>
|
||||
</template>
|
||||
</van-swipe-cell>
|
||||
</van-cell-group>
|
||||
|
||||
<van-empty
|
||||
v-if="!loading && !(transactions && transactions.length)"
|
||||
description="暂无交易记录"
|
||||
/>
|
||||
</van-list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineEmits } from 'vue'
|
||||
|
||||
defineProps({
|
||||
transactions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
finished: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showDelete: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['load', 'click', 'delete'])
|
||||
|
||||
const onLoad = () => {
|
||||
emit('load')
|
||||
}
|
||||
|
||||
const handleClick = (transaction) => {
|
||||
emit('click', transaction)
|
||||
}
|
||||
|
||||
const handleDeleteClick = (transaction) => {
|
||||
emit('delete', transaction)
|
||||
}
|
||||
|
||||
// 获取交易类型名称
|
||||
const getTypeName = (type) => {
|
||||
const typeMap = {
|
||||
0: '支出',
|
||||
1: '收入',
|
||||
2: '不计入收支'
|
||||
}
|
||||
return typeMap[type] || '未知'
|
||||
}
|
||||
|
||||
// 获取交易类型标签类型
|
||||
const getTypeTagType = (type) => {
|
||||
const typeMap = {
|
||||
0: 'danger',
|
||||
1: 'success',
|
||||
2: 'default'
|
||||
}
|
||||
return typeMap[type] || 'default'
|
||||
}
|
||||
|
||||
// 获取金额样式类
|
||||
const getAmountClass = (type) => {
|
||||
if (type === 0) return 'expense'
|
||||
if (type === 1) return 'income'
|
||||
return 'neutral'
|
||||
}
|
||||
|
||||
// 格式化金额(带符号)
|
||||
const formatAmount = (amount, type) => {
|
||||
const formatted = formatMoney(amount)
|
||||
if (type === 0) return `- ${formatted}`
|
||||
if (type === 1) return `+ ${formatted}`
|
||||
return formatted
|
||||
}
|
||||
|
||||
// 格式化金额
|
||||
const formatMoney = (amount) => {
|
||||
return `¥${Number(amount).toFixed(2)}`
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString) return ''
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.transaction-list-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.transaction-card {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.card-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.card-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.transaction-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.reason {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.transaction-info {
|
||||
font-size: 12px;
|
||||
color: #969799;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.transaction-amount {
|
||||
text-align: right;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
min-width: 90px;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.amount.expense {
|
||||
color: #ee0a24;
|
||||
}
|
||||
|
||||
.amount.income {
|
||||
color: #07c160;
|
||||
}
|
||||
|
||||
.amount.neutral {
|
||||
color: #646566;
|
||||
}
|
||||
|
||||
.balance {
|
||||
font-size: 12px;
|
||||
color: #969799;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user