settlementView.vue 10.6 KB
<template>
  <view>
    <scroll-view scroll-y class="detail-content">
      <!-- 结算基本信息 -->
      <view class="card">
        <view class="card-header">
          <view class="header-left">结算信息</view>
        </view>
        <view class="card-body">
          <view class="info-row">
            <text class="label">结算编号</text>
            <text class="value">{{ form.code || '--' }}</text>
          </view>
          <view class="info-row">
            <text class="label">缴费名称</text>
            <text class="value">{{ form.name || '--' }}</text>
          </view>
          <view class="info-row">
            <text class="label">缴费单位</text>
            <text class="value">{{ form.memName || '--' }}</text>
          </view>
          <view class="info-row">
            <text class="label">考试人数</text>
            <text class="value">{{ form.personCount || 0 }}</text>
          </view>
          <view class="info-row">
            <text class="label">费用合计</text>
            <text class="value">¥{{ form.originPrice || 0 }}</text>
          </view>
          <view class="info-row">
            <text class="label">结算金额</text>
            <text class="value text-red">¥{{ form.price || 0 }}</text>
          </view>
          <view class="info-row">
            <text class="label">支付方式</text>
            <text class="value">{{ form.ziZhangBu ? '对公转账' : '民生付' }}</text>
          </view>
          <view class="info-row">
            <text class="label">提交日期</text>
            <text class="value">{{ formatDate(form.commitTime) }}</text>
          </view>
          <view class="info-row">
            <text class="label">结算日期</text>
            <text class="value">{{ formatDate(form.auditTime) }}</text>
          </view>
          <view class="info-row" v-if="form.fileUrl">
            <text class="label">发票</text>
            <text class="value text-primary" @click="downloadInvoice">下载发票</text>
          </view>
        </view>
      </view>

      <!-- 关联缴费单 -->
      <view class="card">
        <view class="card-header">
          <view class="header-left">关联缴费单</view>
        </view>
        <!-- 统计信息 -->
        <view class="payment-stat" v-if="paymentList.length > 0">
          <text>费用合计: <text class="stat-value">¥{{ paymentStat.totalAmount?.toFixed(2) }}</text></text>
          <text>人数合计: <text class="stat-value">{{ paymentStat.totalNum }}</text></text>
        </view>
        <view v-if="loadingPayment" class="state-tip">加载中...</view>
        <view v-else-if="paymentList.length === 0" class="state-tip">暂无关联缴费单</view>
        <view class="payment-list" v-else>
          <view class="payment-item" v-for="(pay, index) in paymentList" :key="pay.payId">
            <!-- 头部:序号 + 名称 + 状态 -->
            <view class="item-header">
              <!-- <view class="item-index">{{ index + 1 }}</view> -->
              <view class="item-name">{{ pay.name }}</view>
              <view :class="['status-tag', pay.submitId ? 'passed' : 'pending']">
                {{ pay.submitId ? '已结算' : '未结算' }}
              </view>
            </view>
            <!-- 主体:单列列表 -->
            <view class="item-body">
              <view class="info-line">
                <text class="info-label">缴费编号</text>
                <text class="info-value">{{ pay.payCode || '--' }}</text>
              </view>
              <view class="info-line">
                <text class="info-label">缴费单位</text>
                <text class="info-value">{{ pay.memberName || '--' }}</text>
              </view>
              <view class="info-line">
                <text class="info-label">考试人数</text>
                <text class="info-value">{{ pay.totalNum || 0 }}</text>
              </view>
              <view class="info-line">
                <text class="info-label">总金额</text>
                <text class="info-value text-red">¥{{ pay.totalAmount || 0 }}</text>
              </view>
              <view class="info-line">
                <text class="info-label">支付方式</text>
                <text class="info-value">民生付</text>
              </view>
              <view class="info-line">
                <text class="info-label">审核状态</text>
                <text :class="['info-value', getVerifyStatusClass(pay.verityStatus)]">{{ pay.verityStatusStr || '--' }}</text>
              </view>
              <view class="info-line">
                <text class="info-label">提交日期</text>
                <text class="info-value">{{ formatDate(pay.submitTime) }}</text>
              </view>
              <view class="info-line">
                <text class="info-label">审核日期</text>
                <text class="info-value">{{ formatDate(pay.verityDate) }}</text>
              </view>
            </view>
          </view>
        </view>
      </view>
    </scroll-view>
  </view>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import * as api from '@/common/api.js'
import config from '@/config.js'

const loading = ref(false)
const loadingPayment = ref(false)
const form = ref({})
const paymentList = ref([])
const paymentStat = ref({
  totalAmount: 0,
  totalNum: 0
})

onLoad((options) => {
  if (options.data) {
    try {
      form.value = JSON.parse(decodeURIComponent(options.data))
    } catch (e) {
      console.error('解析结算数据失败', e)
    }
  }
})

onMounted(() => {
  if (form.value.payids) {
    getPaymentList()
  }
})

async function getPaymentList() {
  loadingPayment.value = true
  paymentStat.value = { totalAmount: 0, totalNum: 0 }
  try {
    const res = await api.settlementDetailList({ payIds: form.value.payids ,type:1 })
    paymentList.value = res.rows || []
    // 计算统计
    let totalAmount = 0
    let totalNum = 0
    for (const pay of paymentList.value) {
      totalAmount += (pay.totalAmount * 1) || 0
      totalNum += (pay.totalNum * 1) || 0
    }
    paymentStat.value = { totalAmount, totalNum }
  } catch (err) {
    console.error('获取缴费单列表失败', err)
    paymentList.value = []
  } finally {
    loadingPayment.value = false
  }
}

function formatDate(dateStr) {
  if (!dateStr) return '--'
  if (typeof dateStr === 'string' && dateStr.indexOf('T') > -1) {
    return dateStr.slice(0, 10)
  }
  return dateStr
}

function getVerifyStatusClass(status) {
  if (status == '1') return 'text-success'
  if (status == '2') return 'text-danger'
  if (status == '3') return 'text-warning'
  return ''
}

function downloadInvoice() {
  if (!form.value.fileUrl) {
    uni.showToast({ title: '暂无发票', icon: 'none' })
    return
  }
  try {
    const invoice = JSON.parse(form.value.fileUrl)
    if (invoice && invoice[0]?.url) {
      let fileUrl = invoice[0].url
      if (!fileUrl.startsWith('http')) {
        fileUrl = config.baseUrl_api + fileUrl
      }
      uni.previewImage({
        urls: [fileUrl],
        success: () => {
          console.log('previewImage success')
        },
        fail: (err) => {
          console.error('previewImage fail:', err)
          uni.showToast({ title: '打开图片失败', icon: 'none' })
        }
      })
    }
  } catch (e) {
    console.error('解析发票数据失败', e)
    uni.showToast({ title: '解析发票数据失败', icon: 'none' })
  }
}
</script>

<style scoped lang="scss">
// 颜色变量
$primary-color: #C4121B;
$success-color: #52c41a;
$bg-color: #f5f5f5;
$card-bg: #ffffff;
$text-primary: #333;
$text-secondary: #666;
$text-placeholder: #999;
$border-color: #eee;

.detail-content {
  padding: 20rpx;
  background: $bg-color;
  min-height: 100vh;
  box-sizing: border-box;
}

.card {
  background: $card-bg;
  border-radius: 16rpx;
  margin-bottom: 20rpx;
  overflow: hidden;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 24rpx 24rpx 20rpx;
  border-bottom: 1rpx solid $border-color;

  .header-left {
    font-size: 30rpx;
    font-weight: 600;
    color: $text-primary;
  }
}

.card-body {
  padding: 8rpx 24rpx 24rpx;
}

.info-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 18rpx 0;
  border-bottom: 1rpx solid #f5f5f5;

  &:last-child {
    border-bottom: none;
  }

  .label {
    font-size: 28rpx;
    color: $text-placeholder;
    flex-shrink: 0;
    width: 170rpx;
  }

  .value {
    font-size: 28rpx;
    color: $text-primary;
    text-align: right;
    flex: 1;
    word-break: break-all;
  }
}

.text-red {
  color: $primary-color !important;
  font-weight: 600;
}

.text-primary {
  color: #1561cb !important;
}

.state-tip {
  text-align: center;
  padding: 60rpx 0;
  font-size: 26rpx;
  color: $text-placeholder;
}

.payment-stat {
  display: flex;
  justify-content: space-between;
  padding: 20rpx 24rpx;
  background: #FFF0F0;
  border-bottom: 1rpx solid $border-color;
  font-size: 26rpx;
  color: $text-secondary;

  .stat-value {
    color: $primary-color;
    font-weight: 600;
  }
}

.payment-list {
  padding: 0 24rpx 24rpx;
}

.payment-item {
  border-radius: 12rpx;
  padding: 24rpx;
  margin-top: 16rpx;
  border: 1rpx solid $border-color;
  background: #fff;

  .item-header {
    display: flex;
    align-items: center;
    margin-bottom: 20rpx;
    padding-bottom: 16rpx;
    border-bottom: 1rpx solid #f5f5f5;

    .item-index {
      width: 40rpx;
      height: 40rpx;
      line-height: 40rpx;
      text-align: center;
      font-size: 22rpx;
      color: #fff;
      background: $primary-color;
      border-radius: 50%;
      margin-right: 16rpx;
    }

    .item-name {
      flex: 1;
      font-size: 28rpx;
      font-weight: 600;
      color: $text-primary;
    }

    .status-tag {
      font-size: 22rpx;
      padding: 4rpx 16rpx;
      border-radius: 20rpx;

      &.passed {
        background: rgba($success-color, 0.1);
        color: $success-color;
      }

      &.pending {
        background: rgba($primary-color, 0.1);
        color: $primary-color;
      }
    }
  }

  .item-body {
    .info-line {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 12rpx 0;
      border-bottom: 1rpx solid #f5f5f5;

      &:last-child {
        border-bottom: none;
      }

      .info-label {
        font-size: 26rpx;
        color: $text-placeholder;
        flex-shrink: 0;
      }

      .info-value {
        font-size: 26rpx;
        color: $text-primary;
        text-align: right;
        flex: 1;
        margin-left: 24rpx;
        word-break: break-all;
      }
    }
  }
}
</style>