| | |
| | | <template> |
| | | <view class="container"> |
| | | <!-- 标题栏 --> |
| | | <Nav title="垫付列表" :customBack="customUrl"></Nav> |
| | | <Nav title="垫付列表" @back="goBack" /> |
| | | |
| | | <!-- 垫付列表-可下拉滚动 --> |
| | | <scroll-view class="advance-scroll" scroll-y="true" style="height: calc(100vh - 80rpx);"> |
| | | <view class="advance-item" v-for="(item, index) in advanceList" :key="index"> |
| | | <view class="item-field"> |
| | | <view class="field-label">调度单号</view> |
| | | <view class="field-value">{{ item.dispatchNo }}</view> |
| | | <!-- 加载状态 --> |
| | | <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="item-field"> |
| | | <view class="field-label">客户</view> |
| | | <view class="field-value">{{ item.customerName }}</view> |
| | | <view class="summary-divider" /> |
| | | <view class="summary-item"> |
| | | <text class="summary-num amount">¥{{ totalAmount }}</text> |
| | | <text class="summary-label">垫付总额</text> |
| | | </view> |
| | | <view class="item-field"> |
| | | <view class="field-label">上传时间</view> |
| | | <view class="field-value">{{ item.feeCreateTime }}</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="item-field"> |
| | | <view class="field-label">垫付费用类型</view> |
| | | <view class="field-value">{{ item.statusStr }}</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="item-field"> |
| | | <view class="field-label">垫付费用金额</view> |
| | | <view class="field-value fee-amount">¥{{ item.actualFeeAmount }}</view> |
| | | </view> |
| | | <!-- 替换原有的voucher-grid部分 --> |
| | | <view class="item-field"> |
| | | <view class="field-label">垫付凭证</view> |
| | | <view class="voucher-grid"> |
| | | <view class="voucher-item" v-for="(img, imgIndex) in item.feeVoucherUrl" :key="imgIndex"> |
| | | <image class="voucher-img" :src="img" mode="aspectFill"></image> |
| | | </view> |
| | | <!-- 当没有图片时显示提示 --> |
| | | <view v-if="!item.feeVoucherUrl || item.feeVoucherUrl.length === 0" class="no-voucher"> |
| | | 暂无凭证 |
| | | </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"; |
| | | import { getTmsFinanceDetailList } from '@/common/paymentList' |
| | | import { getcarType } from '@/common/examine' |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | advanceList: [ |
| | | |
| | | ], |
| | | formData: {}, |
| | | loading: false, |
| | | list: [], |
| | | feeTypeList: [], |
| | | customUrl: 'pages/beReferred/index' |
| | | }; |
| | | 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; |
| | | if (options.id){ |
| | | this.customUrl ='pages/examine/index?id=' + options.id+'&name='+'上传行程'+'&router='+'pages/beReferred/index' |
| | | }else{ |
| | | this.customUrl ='pages/beReferred/index' |
| | | } |
| | | // 获取 URL 参数 |
| | | |
| | | }, created() { |
| | | this.getList(); |
| | | this.formData = options || {} |
| | | this.initData() |
| | | }, |
| | | |
| | | methods: { |
| | | getList() { |
| | | |
| | | |
| | | getcarType('fee_type').then((res) => { |
| | | this.feeTypeList = res |
| | | if (res.length > 0) { |
| | | getTmsFinanceDetailList({dispatchId:this.formData.id}).then((res1) => { |
| | | this.advanceList = res1; |
| | | this.advanceList.forEach(item => { |
| | | // 查找匹配的dictLabel |
| | | const matchedDict = this.feeTypeList.find(dictItem => dictItem.dictValue == item.feeType); |
| | | item.statusStr = matchedDict ? matchedDict.dictLabel : ''; |
| | | |
| | | // 转换voucherUrl为数组 |
| | | if (item.feeVoucherUrl) { |
| | | item.feeVoucherUrl = item.feeVoucherUrl.split(',').filter(url => url.trim() !== ''); |
| | | } else { |
| | | item.feeVoucherUrl = []; |
| | | } |
| | | }); |
| | | |
| | | |
| | | |
| | | |
| | | }).catch(err => { |
| | | console.error('获取调度信息失败:', err); |
| | | }); |
| | | 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 |
| | | } |
| | | |
| | | |
| | | }).catch(err => { |
| | | }); |
| | | } |
| | | 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 scoped> |
| | | <style lang="scss" scoped> |
| | | .container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | height: 100vh; |
| | | background-color: #f7f7f7; |
| | | min-height: 100vh; |
| | | background-color: #f5f7fa; |
| | | } |
| | | |
| | | /* 标题栏 */ |
| | | .title-bar { |
| | | height: 80rpx; |
| | | line-height: 80rpx; |
| | | text-align: center; |
| | | font-size: 32rpx; |
| | | font-weight: 500; |
| | | background-color: #fff; |
| | | border-bottom: 1rpx solid #ccc; |
| | | .center-state { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 60vh; |
| | | } |
| | | |
| | | /* 垫付列表滚动区域 */ |
| | | .advance-scroll { |
| | | .list-scroll { |
| | | flex: 1; |
| | | padding: 20rpx; |
| | | height: calc(100vh - 88rpx); |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | /* 垫付项 */ |
| | | .advance-item { |
| | | background-color: #fff; |
| | | border: 1rpx solid #eee; |
| | | border-radius: 12rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | /* 字段行 */ |
| | | .item-field { |
| | | /* 汇总卡片 */ |
| | | .summary-card { |
| | | display: flex; |
| | | margin-bottom: 20rpx; |
| | | align-items: flex-start; |
| | | 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); |
| | | } |
| | | |
| | | /* 字段标签 */ |
| | | .field-label { |
| | | width: 180rpx; |
| | | color: #666; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | /* 字段值 */ |
| | | .field-value { |
| | | .summary-item { |
| | | flex: 1; |
| | | color: #333; |
| | | font-size: 28rpx; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | } |
| | | |
| | | /* 费用金额特殊样式 */ |
| | | .fee-amount { |
| | | color: #e53e3e; |
| | | .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; |
| | | } |
| | | |
| | | /* 凭证网格 */ |
| | | .voucher-grid { |
| | | display: flex; |
| | | flex-wrap: wrap; /* 允许换行 */ |
| | | gap: 10rpx; |
| | | .pay-time { |
| | | font-size: 24rpx; |
| | | color: #909399; |
| | | } |
| | | |
| | | /* 凭证项 */ |
| | | .voucher-item { |
| | | width: 120rpx; |
| | | height: 120rpx; |
| | | position: relative; |
| | | border: 1rpx solid #eee; |
| | | .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; |
| | | overflow: hidden; |
| | | flex-shrink: 0; /* 防止压缩 */ |
| | | } |
| | | |
| | | /* 凭证图片 */ |
| | | .voucher-img { |
| | | width: 100%; |
| | | height: 100%; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | .pay-info { margin-bottom: 4rpx; } |
| | | |
| | | .pay-info-row { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 10rpx 0; |
| | | } |
| | | /* 添加无凭证提示样式 */ |
| | | .no-voucher { |
| | | color: #999; |
| | | font-size: 28rpx; |
| | | padding: 20rpx 0; |
| | | |
| | | .pay-info-label { |
| | | font-size: 26rpx; |
| | | color: #909399; |
| | | width: 140rpx; |
| | | flex-shrink: 0; |
| | | } |
| | | </style> |
| | | |
| | | .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> |