266 lines
5.6 KiB
Vue
266 lines
5.6 KiB
Vue
<template>
|
|
<view class="records-page">
|
|
<view v-if="records.length" class="records-list">
|
|
<view v-for="item in records" :key="item.trade_no" class="record-card">
|
|
<view class="record-header">
|
|
<text class="record-merchant li-single-line">{{ item.supplier_name || '收款商家' }}</text>
|
|
<text class="record-status" :class="statusClass(item.pay_status)">{{ formatPayStatus(item.pay_status) }}</text>
|
|
</view>
|
|
<view class="record-amount-row">
|
|
<text class="record-amount-label">实付金额</text>
|
|
<text class="record-amount">¥ {{ formatMoney(item.real_money) }}</text>
|
|
</view>
|
|
<view v-if="showWuyeMoney(item)" class="record-meta-row">
|
|
<text class="record-meta-label">物业返还</text>
|
|
<text class="record-meta-value">¥ {{ formatMoney(item.wuye_money) }}</text>
|
|
</view>
|
|
<view class="record-meta-row">
|
|
<text class="record-meta-label">支付方式</text>
|
|
<text class="record-meta-value">{{ formatPayType(item.pay_type) }}</text>
|
|
</view>
|
|
<view class="record-meta-row">
|
|
<text class="record-meta-label">支付时间</text>
|
|
<text class="record-meta-value">{{ item.pay_time || item.create_time || '-' }}</text>
|
|
</view>
|
|
<view class="record-footer">
|
|
<text class="record-trade-no">单号 {{ item.trade_no }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-else-if="!loading" class="records-empty">
|
|
<wd-status-tip :image="emptyImage" tip="暂无付款记录" />
|
|
</view>
|
|
|
|
<view v-if="records.length && (loading || finished)" class="records-footer">
|
|
<wd-loadmore :state="loadMoreState" />
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref } from 'vue';
|
|
import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app';
|
|
import { tradeList } from '@/api/cashier';
|
|
import type { TradeListItem } from '@/api/cashier';
|
|
import { APP_CONFIG } from '@/config';
|
|
|
|
const PAGE_SIZE = 10;
|
|
|
|
const records = ref<TradeListItem[]>([]);
|
|
const page = ref(1);
|
|
const loading = ref(false);
|
|
const finished = ref(false);
|
|
|
|
const emptyImage = `${APP_CONFIG.RESOURCE_URL}tip/message.png`;
|
|
|
|
const loadMoreState = computed(() => (loading.value ? 'loading' : 'finished'));
|
|
|
|
const formatMoney = (value?: string | number) => {
|
|
const amount = Number(value || 0);
|
|
if (!Number.isFinite(amount)) return '0.00';
|
|
return amount.toFixed(2);
|
|
};
|
|
|
|
const formatPayStatus = (status?: number) => {
|
|
const map: Record<number, string> = {
|
|
0: '未支付',
|
|
1: '已支付',
|
|
2: '部分退款',
|
|
3: '全额退款',
|
|
4: '已取消'
|
|
};
|
|
return map[Number(status)] || '未知状态';
|
|
};
|
|
|
|
const statusClass = (status?: number) => {
|
|
const value = Number(status);
|
|
if (value === 1) return 'status-success';
|
|
if (value === 0) return 'status-pending';
|
|
if (value === 4) return 'status-cancel';
|
|
return 'status-refund';
|
|
};
|
|
|
|
const formatPayType = (payType?: number) => {
|
|
const map: Record<number, string> = {
|
|
1: '微信',
|
|
2: '支付宝',
|
|
3: '云闪付',
|
|
4: '现金',
|
|
5: '余额',
|
|
6: '线下'
|
|
};
|
|
return map[Number(payType)] || '-';
|
|
};
|
|
|
|
const showWuyeMoney = (item: TradeListItem) => Number(item.wuye_money || 0) > 0;
|
|
|
|
const fetchRecords = async (reset = false) => {
|
|
if (loading.value) return;
|
|
if (!reset && finished.value) return;
|
|
|
|
loading.value = true;
|
|
const nextPage = reset ? 1 : page.value;
|
|
|
|
try {
|
|
const response = await tradeList({
|
|
trade_no: '',
|
|
page: nextPage,
|
|
limit: PAGE_SIZE
|
|
});
|
|
|
|
if (Number(response?.code) !== 200) {
|
|
throw new Error(response?.msg || '获取付款记录失败');
|
|
}
|
|
|
|
const list = Array.isArray(response?.data) ? response.data : [];
|
|
records.value = reset ? list : records.value.concat(list);
|
|
page.value = nextPage + 1;
|
|
finished.value = list.length < PAGE_SIZE;
|
|
} catch (error: any) {
|
|
uni.showToast({
|
|
title: error?.message || '获取付款记录失败',
|
|
icon: 'none'
|
|
});
|
|
} finally {
|
|
loading.value = false;
|
|
if (reset) {
|
|
uni.stopPullDownRefresh();
|
|
}
|
|
}
|
|
};
|
|
|
|
onLoad(() => {
|
|
fetchRecords(true);
|
|
});
|
|
|
|
onPullDownRefresh(() => {
|
|
finished.value = false;
|
|
fetchRecords(true);
|
|
});
|
|
|
|
onReachBottom(() => {
|
|
fetchRecords(false);
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.records-page {
|
|
min-height: 100vh;
|
|
padding: 24rpx;
|
|
background: #f4f6f8;
|
|
}
|
|
|
|
.records-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 20rpx;
|
|
}
|
|
|
|
.record-card {
|
|
padding: 28rpx 28rpx 24rpx;
|
|
border-radius: 16rpx;
|
|
background: #ffffff;
|
|
box-shadow: 0 8rpx 22rpx rgba(17, 24, 39, 0.04);
|
|
}
|
|
|
|
.record-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 20rpx;
|
|
}
|
|
|
|
.record-merchant {
|
|
flex: 1;
|
|
min-width: 0;
|
|
color: #111827;
|
|
font-size: 30rpx;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.record-status {
|
|
flex-shrink: 0;
|
|
padding: 6rpx 16rpx;
|
|
border-radius: 999rpx;
|
|
font-size: 22rpx;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.status-success {
|
|
background: #e8f2ff;
|
|
color: #1f5bd8;
|
|
}
|
|
|
|
.status-pending {
|
|
background: #fff7e8;
|
|
color: #d48806;
|
|
}
|
|
|
|
.status-cancel {
|
|
background: #f3f4f6;
|
|
color: #6b7280;
|
|
}
|
|
|
|
.status-refund {
|
|
background: #fff1f0;
|
|
color: #cf1322;
|
|
}
|
|
|
|
.record-amount-row {
|
|
display: flex;
|
|
align-items: baseline;
|
|
justify-content: space-between;
|
|
margin-top: 24rpx;
|
|
}
|
|
|
|
.record-amount-label {
|
|
color: #8b929e;
|
|
font-size: 26rpx;
|
|
}
|
|
|
|
.record-amount {
|
|
color: #111827;
|
|
font-size: 40rpx;
|
|
font-weight: 800;
|
|
}
|
|
|
|
.record-meta-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-top: 18rpx;
|
|
}
|
|
|
|
.record-meta-label {
|
|
color: #8b929e;
|
|
font-size: 26rpx;
|
|
}
|
|
|
|
.record-meta-value {
|
|
max-width: 420rpx;
|
|
color: #374151;
|
|
font-size: 26rpx;
|
|
text-align: right;
|
|
}
|
|
|
|
.record-footer {
|
|
margin-top: 22rpx;
|
|
padding-top: 20rpx;
|
|
border-top: 1rpx solid #eef1f5;
|
|
}
|
|
|
|
.record-trade-no {
|
|
color: #9ca3af;
|
|
font-size: 22rpx;
|
|
}
|
|
|
|
.records-empty {
|
|
padding-top: 300rpx;
|
|
}
|
|
|
|
.records-footer {
|
|
padding: 12rpx 0 24rpx;
|
|
}
|
|
</style>
|