sen
2026-03-31 cc41746818af3b619167947ecb4401ccdfbec2d8
ui/car_wx_app/pages/paymentList/index.vue
@@ -1,210 +1,405 @@
<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>