fix: 活动记录,汇总费用。

This commit is contained in:
tianyongbao
2026-05-26 00:33:59 +08:00
parent 65ff9768c7
commit 984b837a79
2 changed files with 411 additions and 2 deletions

View File

@@ -0,0 +1,10 @@
import request from '@/utils/request'
// 查询账户交易记录列表
export function listAccountDealRecord(query) {
return request({
url: '/invest/accountDealRecord/list',
method: 'get',
params: query
})
}

View File

@@ -88,6 +88,10 @@
</view>
<view class="operate" @click.stop>
<view class="btn-summary" @click="handleCostSummary(item)">
<uni-icons type="list" size="16" color="#13c2c2"></uni-icons>
<text>费用汇总</text>
</view>
<view class="btn-edit" @click="handleEdit(item)">
<uni-icons type="compose" size="16" color="#667eea"></uni-icons>
<text>修改</text>
@@ -107,13 +111,71 @@
</view>
<u-loadmore :status="status" loadingIcon="semicircle" height="88" fontSize="32rpx" @loadmore="loadmore" />
</u-list>
<view v-if="summaryVisible" class="summary-mask" @click="closeSummary">
<view class="summary-panel" @click.stop>
<view class="summary-header">
<view class="summary-title">
<text class="summary-name">费用汇总</text>
<text class="summary-subtitle">{{ currentActivity.name || '--' }}</text>
</view>
<view class="summary-close" @click="closeSummary">
<uni-icons type="closeempty" size="24" color="#909399"></uni-icons>
</view>
</view>
<view class="summary-meta">
<view class="summary-meta-item">
<text class="meta-label">已选</text>
<text class="meta-value">{{ selectedExpenses.length }} </text>
</view>
<view class="summary-meta-item">
<text class="meta-label">合计金额</text>
<text class="meta-value amount">{{ selectedTotalAmount }} </text>
</view>
</view>
<scroll-view scroll-y class="summary-scroll">
<view v-if="summaryLoading" class="summary-empty">加载中...</view>
<view v-else-if="expenseList.length === 0" class="summary-empty">暂无可汇总支出</view>
<block v-else>
<view
v-for="expense in expenseList"
:key="expense.id"
class="expense-item"
:class="{ selected: isExpenseSelected(expense) }"
@click="toggleExpense(expense)"
>
<view class="expense-checkbox" :class="{ active: isExpenseSelected(expense) }"></view>
<view class="expense-content">
<view class="expense-main">
<text class="expense-category">{{ expense.childCategoryName || dictStr(expense.dealCategory, dealCategoryList) || '日常支出' }}</text>
<text class="expense-amount">{{ expense.amount || 0 }} </text>
</view>
<view class="expense-sub">
<text>{{ expense.accountName || '--' }}</text>
<text>{{ expense.createTime || '--' }}</text>
</view>
<text v-if="expense.remark" class="expense-remark">{{ expense.remark }}</text>
</view>
</view>
</block>
</scroll-view>
<view class="summary-footer">
<view class="summary-cancel" @click="closeSummary">取消</view>
<view class="summary-confirm" :class="{ disabled: selectedExpenses.length === 0 }" @click="confirmSummary">确认汇总</view>
</view>
</view>
</view>
</view>
<!--返回首页按钮 -->
<suspend></suspend>
</template>
<script setup>
import { listActivity, delActivity } from '@/api/health/activity'
import { listActivity, getActivity, delActivity, updateActivity } from '@/api/health/activity'
import { listAccountDealRecord } from '@/api/invest/accountDealRecord'
import { getDicts } from '@/api/system/dict/data.js'
import { timeHandler } from '@/utils/common.ts'
import {onLoad,onShow} from "@dcloudio/uni-app";
@@ -126,6 +188,13 @@ const isShow = ref(false)
const status = ref('loadmore')
const typeList = ref([])
const activityVolumeList = ref([])
const dealCategoryList = ref([])
const summaryVisible = ref(false)
const summaryLoading = ref(false)
const expenseList = ref([])
const selectedExpenses = ref([])
const currentActivityId = ref(null)
const currentActivity = ref({})
const flag= ref(true)
@@ -141,6 +210,13 @@ const { filterPanel, queryParams} = toRefs(data)
const windowHeight = computed(() => {
uni.getSystemInfoSync().windowHeight - 50
})
const selectedTotalAmount = computed(() => {
return selectedExpenses.value
.reduce((sum, item) => {
return sum + (parseFloat(item.amount) || 0)
}, 0)
.toFixed(2)
})
onLoad(() => {
getList()
});
@@ -184,6 +260,9 @@ function getList() {
getDicts('activity_exercise').then(res => {
activityVolumeList.value = res.data
})
getDicts('deal_category').then(res => {
dealCategoryList.value = res.data
})
status.value = 'loading'
listActivity({ pageSize: 10, pageNum: pageNum.value, ...queryParams.value }).then(res => {
listData.value = listData.value.concat(res.rows)
@@ -209,6 +288,104 @@ function getList() {
uni.navigateTo({ url: `/pages/health/activity/addEdit` })
isShow.value = true
}
function handleCostSummary(item) {
currentActivityId.value = item.id
currentActivity.value = item
selectedExpenses.value = []
expenseList.value = []
summaryVisible.value = true
summaryLoading.value = true
const queryExpenseParams = {
pageNum: 1,
pageSize: 1000,
dealCategory: '1',
startTime: item.startTime ? dayjs(item.startTime).format('YYYY-MM-DD') : '',
endTime: item.endTime ? dayjs(item.endTime).format('YYYY-MM-DD') : dayjs().format('YYYY-MM-DD')
}
listAccountDealRecord(queryExpenseParams).then(res => {
expenseList.value = res.rows || []
summaryLoading.value = false
}).catch(() => {
summaryLoading.value = false
})
}
function closeSummary() {
summaryVisible.value = false
selectedExpenses.value = []
expenseList.value = []
}
function isExpenseSelected(item) {
return selectedExpenses.value.some(expense => expense.id === item.id)
}
function toggleExpense(item) {
if (isExpenseSelected(item)) {
selectedExpenses.value = selectedExpenses.value.filter(expense => expense.id !== item.id)
} else {
selectedExpenses.value.push(item)
}
}
function buildCostDetail(activity) {
const startDate = activity.startTime ? dayjs(activity.startTime).format('YYYY-MM-DD') : ''
const endDate = activity.endTime ? dayjs(activity.endTime).format('YYYY-MM-DD') : ''
const isMultiDay = startDate !== endDate
if (isMultiDay) {
const groupedByDate = {}
selectedExpenses.value.forEach(item => {
const date = dayjs(item.createTime).format('MM-DD')
if (!groupedByDate[date]) {
groupedByDate[date] = []
}
const category = item.childCategoryName || ''
groupedByDate[date].push(`${category} ${item.amount}${item.remark ? '(' + item.remark + ')' : ''}`)
})
return Object.keys(groupedByDate)
.sort()
.map(date => `${date} ${groupedByDate[date].join('、')}`)
.join('')
}
return selectedExpenses.value
.map(item => {
const category = item.childCategoryName || ''
return `${category} ${item.amount}${item.remark ? '(' + item.remark + ')' : ''}`
})
.join('')
}
function confirmSummary() {
if (selectedExpenses.value.length === 0) {
uni.showToast({
title: '请选择费用记录',
icon: 'none'
})
return
}
getActivity(currentActivityId.value).then(res => {
const activity = res.data
const totalAmount = parseFloat(selectedTotalAmount.value)
const currentTotal = parseFloat(activity.totalCost) || 0
const detailsWithDate = buildCostDetail(activity)
const updateData = {
...activity,
totalCost: (currentTotal + totalAmount).toFixed(2),
costDetail: activity.costDetail ? `${activity.costDetail}${detailsWithDate}` : detailsWithDate
}
updateActivity(updateData).then(() => {
uni.showToast({
title: '汇总成功',
icon: 'success'
})
closeSummary()
pageNum.value = 1
listData.value = []
getList()
})
})
}
function handleDelete(item) {
uni.showModal({
title: '确认删除',
@@ -530,7 +707,9 @@ page {
justify-content: flex-end;
padding: 16rpx 24rpx 24rpx;
gap: 16rpx;
flex-wrap: wrap;
.btn-summary,
.btn-edit,
.btn-copy,
.btn-delete {
@@ -549,6 +728,12 @@ page {
transform: scale(0.95);
}
}
.btn-summary {
background: rgba(19, 194, 194, 0.1);
color: #13c2c2;
border: 1rpx solid rgba(19, 194, 194, 0.3);
}
.btn-edit {
background: rgba(102, 126, 234, 0.1);
@@ -569,4 +754,218 @@ page {
}
}
}
</style>
.summary-mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 999;
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: flex-end;
}
.summary-panel {
width: 100%;
max-height: 82vh;
background: #ffffff;
border-radius: 24rpx 24rpx 0 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
.summary-header {
padding: 24rpx 28rpx;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1rpx solid #ebeef5;
}
.summary-title {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 6rpx;
}
.summary-name {
color: #2c3e50;
font-size: 32rpx;
font-weight: 700;
}
.summary-subtitle {
color: #909399;
font-size: 24rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.summary-close {
width: 56rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.summary-meta {
padding: 18rpx 28rpx;
background: #fafbfc;
display: flex;
gap: 20rpx;
}
.summary-meta-item {
flex: 1;
display: flex;
flex-direction: column;
gap: 6rpx;
min-width: 0;
}
.meta-label {
color: #909399;
font-size: 24rpx;
}
.meta-value {
color: #2c3e50;
font-size: 30rpx;
font-weight: 700;
&.amount {
color: #f5576c;
}
}
.summary-scroll {
height: 58vh;
padding: 16rpx 24rpx;
box-sizing: border-box;
}
.summary-empty {
color: #909399;
font-size: 28rpx;
text-align: center;
padding: 120rpx 0;
}
.expense-item {
display: flex;
gap: 16rpx;
padding: 18rpx;
margin-bottom: 16rpx;
border: 2rpx solid #ebeef5;
border-radius: 14rpx;
background: #ffffff;
&.selected {
border-color: rgba(102, 126, 234, 0.55);
background: rgba(102, 126, 234, 0.06);
}
}
.expense-checkbox {
width: 34rpx;
height: 34rpx;
margin-top: 4rpx;
border-radius: 50%;
border: 3rpx solid #dcdfe6;
flex-shrink: 0;
&.active {
border-color: #667eea;
background: #667eea;
box-shadow: inset 0 0 0 7rpx #ffffff;
}
}
.expense-content {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 10rpx;
}
.expense-main {
display: flex;
justify-content: space-between;
gap: 16rpx;
align-items: center;
}
.expense-category {
color: #2c3e50;
font-size: 28rpx;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.expense-amount {
color: #f5576c;
font-size: 28rpx;
font-weight: 700;
flex-shrink: 0;
}
.expense-sub {
display: flex;
justify-content: space-between;
gap: 16rpx;
color: #909399;
font-size: 24rpx;
}
.expense-remark {
color: #606266;
font-size: 24rpx;
line-height: 1.5;
word-break: break-all;
}
.summary-footer {
padding: 18rpx 24rpx 28rpx;
display: flex;
gap: 16rpx;
border-top: 1rpx solid #ebeef5;
}
.summary-cancel,
.summary-confirm {
flex: 1;
height: 78rpx;
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
font-weight: 600;
}
.summary-cancel {
color: #606266;
background: #f5f7fa;
border: 2rpx solid #dcdfe6;
}
.summary-confirm {
color: #ffffff;
background: #667eea;
&.disabled {
opacity: 0.5;
}
}
</style>