<template>
|
<view class="container">
|
<Nav title="垫付列表" @back="goBack" />
|
|
<!-- 加载状态 -->
|
<view v-if="loading && !list.length" class="center-state">
|
<u-loading-icon size="32" text="加载中..." />
|
</view>
|
|
<!-- 空状态 -->
|
<view v-else-if="!loading && !list.length" class="center-state">
|
<u-empty mode="data" text="暂无垫付记录" />
|
</view>
|
|
<!-- 列表 -->
|
<scroll-view
|
v-else
|
class="list-scroll"
|
scroll-y
|
@scrolltolower="loadMore"
|
>
|
<!-- 汇总卡片 -->
|
<view class="summary-card">
|
<view class="summary-item">
|
<text class="summary-num">{{ total }}</text>
|
<text class="summary-label">垫付笔数</text>
|
</view>
|
<view class="summary-divider" />
|
<view class="summary-item">
|
<text class="summary-num amount">¥{{ totalAmount }}</text>
|
<text class="summary-label">垫付总额</text>
|
</view>
|
</view>
|
|
<!-- 垫付记录列表 -->
|
<view
|
v-for="(item, index) in list"
|
:key="index"
|
class="pay-card"
|
>
|
<!-- 卡片头部 -->
|
<view class="pay-header">
|
<view class="pay-type-tag">{{ item.statusStr }}</view>
|
<text class="pay-time">{{ item.feeCreateTime }}</text>
|
</view>
|
|
<!-- 金额行 -->
|
<view class="pay-amount-row">
|
<text class="pay-amount">¥{{ item.actualFeeAmount || 0 }}</text>
|
<text v-if="item.currencyStr" class="pay-currency">{{ item.currencyStr }}</text>
|
</view>
|
|
<!-- 信息行 -->
|
<view class="pay-info">
|
<view class="pay-info-row">
|
<text class="pay-info-label">调度单号</text>
|
<text class="pay-info-value">{{ item.dispatchNo || '-' }}</text>
|
</view>
|
<view class="pay-info-row">
|
<text class="pay-info-label">客户</text>
|
<text class="pay-info-value">{{ item.customerName || '-' }}</text>
|
</view>
|
</view>
|
|
<!-- 凭证图片:用 index 传参,不传数组 -->
|
<view v-if="item.images && item.images.length" class="pay-images">
|
<text class="pay-images-label">垫付凭证</text>
|
<view class="pay-images-grid">
|
<image
|
v-for="(img, i) in item.images"
|
:key="i"
|
class="pay-thumb"
|
:src="img"
|
mode="aspectFill"
|
lazy-load
|
@tap="onPreview(index, i)"
|
/>
|
</view>
|
</view>
|
</view>
|
|
<!-- 底部加载状态 -->
|
<view class="load-more">
|
<u-loadmore :status="loadStatus" />
|
</view>
|
</scroll-view>
|
</view>
|
</template>
|
|
<script>
|
import { getTmsFinanceDetailList } from '@/common/paymentList'
|
import { getcarType } from '@/common/examine'
|
|
export default {
|
data() {
|
return {
|
loading: false,
|
list: [],
|
feeTypeList: [],
|
currencyList: [],
|
formData: {},
|
// 分页
|
pageNum: 1,
|
pageSize: 10,
|
total: 0,
|
totalAmount: '0.00',
|
finished: false
|
}
|
},
|
|
computed: {
|
loadStatus() {
|
if (this.loading) return 'loading'
|
if (this.finished) return 'nomore'
|
return 'loadmore'
|
}
|
},
|
|
onLoad(options) {
|
this.formData = options || {}
|
this.initData()
|
},
|
|
methods: {
|
goBack() {
|
const pages = getCurrentPages()
|
for (let i = pages.length - 2; i >= 0; i--) {
|
if (pages[i].route && pages[i].route.includes('pages/examine')) {
|
uni.navigateBack({ delta: pages.length - 1 - i })
|
return
|
}
|
}
|
uni.navigateBack({ delta: 1 })
|
},
|
|
getDictLabel(dictList, value) {
|
const found = dictList.find(d => d.dictValue == value)
|
return found?.dictLabel || ''
|
},
|
|
parseImages(urlStr) {
|
if (!urlStr) return []
|
return urlStr.split(',').filter(u => u.trim())
|
},
|
|
// 首次加载:字典 + 第一页数据
|
async initData() {
|
this.loading = true
|
try {
|
const [feeTypes, currencyTypes] = await Promise.all([
|
getcarType('fee_type'),
|
getcarType('sys_currency')
|
])
|
this.feeTypeList = feeTypes || []
|
this.currencyList = currencyTypes || []
|
await this.loadPage()
|
} catch {
|
uni.$u.toast('加载失败')
|
} finally {
|
this.loading = false
|
}
|
},
|
|
// 加载一页数据(无 id 时查全部)
|
async loadPage() {
|
this.loading = true
|
try {
|
const params = { pageNum: this.pageNum, pageSize: this.pageSize }
|
if (this.formData.id) {
|
params.dispatchId = this.formData.id
|
}
|
const res = await getTmsFinanceDetailList(params)
|
|
// 兼容分页和非分页两种后端返回格式
|
let rows, total, totalAmount
|
if (res && res.rows) {
|
// 分页格式:{ rows: [], total: 0, totalAmount: 0 }
|
rows = res.rows || []
|
total = res.total || 0
|
totalAmount = res.totalAmount
|
} else {
|
// 非分页格式(后端未改时兼容):直接返回数组
|
rows = Array.isArray(res) ? res : []
|
total = rows.length
|
this.finished = true
|
}
|
|
const processed = rows.map(item => ({
|
...item,
|
statusStr: this.getDictLabel(this.feeTypeList, item.feeType),
|
currencyStr: this.getDictLabel(this.currencyList, item.currency),
|
images: this.parseImages(item.feeVoucherUrl)
|
}))
|
|
if (this.pageNum === 1) {
|
this.list = processed
|
} else {
|
this.list = this.list.concat(processed)
|
}
|
|
this.total = total
|
if (totalAmount !== undefined) {
|
this.totalAmount = Number(totalAmount).toFixed(2)
|
} else {
|
// 非分页时前端自己算
|
const sum = this.list.reduce((acc, i) => acc + (Number(i.actualFeeAmount) || 0), 0)
|
this.totalAmount = sum.toFixed(2)
|
}
|
|
// 判断是否还有更多
|
if (this.list.length >= total || rows.length < this.pageSize) {
|
this.finished = true
|
}
|
} catch {
|
uni.$u.toast('加载失败')
|
} finally {
|
this.loading = false
|
}
|
},
|
|
// 上拉加载更多
|
loadMore() {
|
if (this.loading || this.finished) return
|
this.pageNum++
|
this.loadPage()
|
},
|
|
// 图片预览:通过索引取数据,避免小程序模板传数组的坑
|
onPreview(itemIndex, imgIndex) {
|
const item = this.list[itemIndex]
|
if (item && item.images && item.images.length) {
|
uni.previewImage({
|
urls: item.images,
|
current: item.images[imgIndex]
|
})
|
}
|
}
|
}
|
}
|
</script>
|
|
<style lang="scss" scoped>
|
.container {
|
display: flex;
|
flex-direction: column;
|
min-height: 100vh;
|
background-color: #f5f7fa;
|
}
|
|
.center-state {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
height: 60vh;
|
}
|
|
.list-scroll {
|
flex: 1;
|
height: calc(100vh - 88rpx);
|
box-sizing: border-box;
|
}
|
|
/* 汇总卡片 */
|
.summary-card {
|
display: flex;
|
align-items: center;
|
background: linear-gradient(135deg, #4285f4 0%, #5b9ef4 100%);
|
margin: 24rpx 24rpx 0;
|
padding: 36rpx 0;
|
border-radius: 16rpx;
|
box-shadow: 0 8rpx 24rpx rgba(66, 133, 244, 0.25);
|
}
|
|
.summary-item {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
}
|
|
.summary-num {
|
font-size: 40rpx;
|
font-weight: 700;
|
color: #fff;
|
margin-bottom: 8rpx;
|
|
&.amount { font-size: 36rpx; }
|
}
|
|
.summary-label {
|
font-size: 24rpx;
|
color: rgba(255, 255, 255, 0.8);
|
}
|
|
.summary-divider {
|
width: 1rpx;
|
height: 60rpx;
|
background-color: rgba(255, 255, 255, 0.3);
|
}
|
|
/* 垫付卡片 */
|
.pay-card {
|
background: #fff;
|
margin: 20rpx 24rpx 0;
|
border-radius: 16rpx;
|
padding: 28rpx;
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
}
|
|
.pay-header {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
margin-bottom: 20rpx;
|
}
|
|
.pay-type-tag {
|
padding: 6rpx 20rpx;
|
border-radius: 8rpx;
|
font-size: 24rpx;
|
font-weight: 500;
|
background-color: rgba(230, 162, 60, 0.1);
|
color: #e6a23c;
|
}
|
|
.pay-time {
|
font-size: 24rpx;
|
color: #909399;
|
}
|
|
.pay-amount-row {
|
display: flex;
|
align-items: baseline;
|
gap: 12rpx;
|
margin-bottom: 24rpx;
|
padding-bottom: 24rpx;
|
border-bottom: 1rpx solid #f2f3f5;
|
}
|
|
.pay-amount {
|
font-size: 44rpx;
|
font-weight: 700;
|
color: #f56c6c;
|
}
|
|
.pay-currency {
|
font-size: 24rpx;
|
color: #909399;
|
padding: 4rpx 12rpx;
|
background-color: #f5f7fa;
|
border-radius: 6rpx;
|
}
|
|
.pay-info { margin-bottom: 4rpx; }
|
|
.pay-info-row {
|
display: flex;
|
align-items: center;
|
padding: 10rpx 0;
|
}
|
|
.pay-info-label {
|
font-size: 26rpx;
|
color: #909399;
|
width: 140rpx;
|
flex-shrink: 0;
|
}
|
|
.pay-info-value {
|
font-size: 26rpx;
|
color: #303133;
|
flex: 1;
|
}
|
|
.pay-images {
|
margin-top: 20rpx;
|
padding-top: 20rpx;
|
border-top: 1rpx solid #f2f3f5;
|
}
|
|
.pay-images-label {
|
font-size: 26rpx;
|
color: #909399;
|
margin-bottom: 16rpx;
|
display: block;
|
}
|
|
.pay-images-grid {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 16rpx;
|
}
|
|
.pay-thumb {
|
width: 140rpx;
|
height: 140rpx;
|
border-radius: 10rpx;
|
border: 1rpx solid #ebeef5;
|
background-color: #f5f7fa;
|
}
|
|
.load-more {
|
padding: 30rpx 0 50rpx;
|
}
|
</style>
|