|
|
|
|
|
<template>
|
|
|
|
|
|
<view class="reservation-page">
|
|
|
|
|
|
<view class="fixed-nav">
|
|
|
|
|
|
<NavBar title="发票管理">
|
|
|
|
|
|
</NavBar>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="content-area">
|
|
|
|
|
|
<template v-if="invoiceList.length > 0">
|
|
|
|
|
|
<view v-for="item in invoiceList" :key="item.id" class="invoice-card">
|
|
|
|
|
|
<view class="invoice-header">
|
|
|
|
|
|
<view class="status-tag pending">待开具</view>
|
|
|
|
|
|
<view class="invoice-date">{{ formatChinaDate(item.created_at) }}</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="invoice-title">{{ item.direction_name }}</view>
|
|
|
|
|
|
<view class="invoice-amount">¥{{ (item.price || 0) }}</view>
|
|
|
|
|
|
<view class="invoice-batch">批次号:{{ item.batch && item.batch.name ? item.batch.name : '-' }}</view>
|
|
|
|
|
|
<view class="invoice-ship">船舶编号:{{ item.ship ? item.ship.ship_number : '-' }}</view>
|
|
|
|
|
|
<view class="invoice-actions single-btn">
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="invoice-detail-btn issue"
|
|
|
|
|
|
@click="handleInvoiceAction(item)"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ item.bill_status === 3 ? '查看发票' : '去开票' }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else>
|
|
|
|
|
|
<view class="empty-box">
|
|
|
|
|
|
<image src="/static/empty.png" class="empty-img" mode="aspectFit" />
|
|
|
|
|
|
<view class="empty-text">暂无发票</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<!-- 加载更多提示 -->
|
|
|
|
|
|
<view v-if="invoiceList.length > 0" class="load-more">
|
|
|
|
|
|
<view v-if="loading" class="loading-text">加载中...</view>
|
|
|
|
|
|
<view v-else-if="!hasMore" class="no-more-text">没有更多数据了</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import NavBar from '@/components/NavBar.vue'
|
|
|
|
|
|
import { base } from '@/common/util.js'
|
|
|
|
|
|
import { API } from '@/config/index.js'
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'InvoiceManagePage',
|
|
|
|
|
|
components: { NavBar },
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
invoiceList: [],
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
lastPage: 1,
|
|
|
|
|
|
loading: false,
|
|
|
|
|
|
hasMore: true
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
onShow() {
|
|
|
|
|
|
// 重置分页
|
|
|
|
|
|
this.page = 1
|
|
|
|
|
|
this.hasMore = true
|
|
|
|
|
|
this.fetchInvoiceList(true)
|
|
|
|
|
|
},
|
|
|
|
|
|
// 下拉刷新
|
|
|
|
|
|
onPullDownRefresh() {
|
|
|
|
|
|
this.page = 1
|
|
|
|
|
|
this.hasMore = true
|
|
|
|
|
|
this.fetchInvoiceList(true).finally(() => {
|
|
|
|
|
|
uni.stopPullDownRefresh()
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
// 上拉加载更多
|
|
|
|
|
|
onReachBottom() {
|
|
|
|
|
|
if (this.hasMore && !this.loading) {
|
|
|
|
|
|
this.loadMore()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
formatChinaDate: base.formatChinaDate,
|
|
|
|
|
|
async fetchInvoiceList(reset = false) {
|
|
|
|
|
|
const token = uni.getStorageSync('token')
|
|
|
|
|
|
if (!token) return
|
|
|
|
|
|
|
|
|
|
|
|
// 如果正在加载,直接返回
|
|
|
|
|
|
if (this.loading) return
|
|
|
|
|
|
|
|
|
|
|
|
this.loading = true
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await new Promise((resolve, reject) => {
|
|
|
|
|
|
uni.request({
|
|
|
|
|
|
url: `${API.RESERVATION_LIST}?token=${token}`,
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
data: {
|
|
|
|
|
|
page: this.page,
|
|
|
|
|
|
page_size: 10,
|
|
|
|
|
|
bill_status: 4 // 只获取待开票的订单
|
|
|
|
|
|
},
|
|
|
|
|
|
success: resolve,
|
|
|
|
|
|
fail: reject
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (res.data && res.data.errcode === 0) {
|
|
|
|
|
|
const newList = res.data.data.data || []
|
|
|
|
|
|
this.lastPage = res.data.data.last_page || 1
|
|
|
|
|
|
|
|
|
|
|
|
if (reset) {
|
|
|
|
|
|
// 重置列表
|
|
|
|
|
|
this.invoiceList = newList
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 追加数据
|
|
|
|
|
|
this.invoiceList = [...this.invoiceList, ...newList]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断是否还有更多数据
|
|
|
|
|
|
this.hasMore = this.page < this.lastPage
|
|
|
|
|
|
} else {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: res.data.errmsg || '获取发票列表失败',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('获取发票列表失败:', e)
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '网络错误',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.loading = false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
// 加载更多
|
|
|
|
|
|
loadMore() {
|
|
|
|
|
|
if (this.hasMore && !this.loading) {
|
|
|
|
|
|
this.page += 1
|
|
|
|
|
|
this.fetchInvoiceList(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
handleInvoiceAction(item) {
|
|
|
|
|
|
if (!item) return
|
|
|
|
|
|
|
|
|
|
|
|
if (item.bill_status === 3) {
|
|
|
|
|
|
const bill = item.bill || {}
|
|
|
|
|
|
const billInfoList = Array.isArray(bill.billInfoList) ? bill.billInfoList : []
|
|
|
|
|
|
const firstInfo = billInfoList[0] || {}
|
|
|
|
|
|
const pictureUrl = firstInfo.pictureUrl || ''
|
|
|
|
|
|
|
|
|
|
|
|
if (pictureUrl) {
|
|
|
|
|
|
this.openExternalLink(pictureUrl)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
uni.showToast({ title: '暂无发票链接', icon: 'none' })
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.goInvoice(item)
|
|
|
|
|
|
},
|
|
|
|
|
|
goInvoice(item) {
|
|
|
|
|
|
const token = uni.getStorageSync('token')
|
|
|
|
|
|
if (!token) {
|
|
|
|
|
|
uni.showToast({ title: '请先登录', icon: 'none' })
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!item || !item.id) {
|
|
|
|
|
|
uni.showToast({ title: '订单信息缺失', icon: 'none' })
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
uni.showLoading({ title: '提交中...' })
|
|
|
|
|
|
uni.request({
|
|
|
|
|
|
url: `${API.GET_INVOICE}`,
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
data: {
|
|
|
|
|
|
reservation_id: item.id,
|
|
|
|
|
|
token
|
|
|
|
|
|
},
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
|
if (res.data && res.data.errcode === 0) {
|
|
|
|
|
|
const billData = res.data && res.data.data
|
|
|
|
|
|
const billInfo = billData && billData.bill
|
|
|
|
|
|
const billInfoList = billInfo && billInfo.billInfoList
|
|
|
|
|
|
const pictureUrl = Array.isArray(billInfoList) && billInfoList.length > 0
|
|
|
|
|
|
? billInfoList[0].pictureUrl
|
|
|
|
|
|
: ''
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '开票申请已提交',
|
|
|
|
|
|
icon: 'success',
|
|
|
|
|
|
duration: 2000,
|
|
|
|
|
|
success: () => {
|
|
|
|
|
|
if (pictureUrl) {
|
|
|
|
|
|
this.openExternalLink(pictureUrl)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
this.page = 1
|
|
|
|
|
|
this.hasMore = true
|
|
|
|
|
|
this.fetchInvoiceList(true)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
uni.showToast({ title: (res.data && res.data.errmsg) || '提交失败', icon: 'none' })
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: () => {
|
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
|
uni.showToast({ title: '提交失败', icon: 'none' })
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
openExternalLink(url) {
|
|
|
|
|
|
if (!url) return
|
|
|
|
|
|
window.open(url, '_blank')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.reservation-page {
|
|
|
|
|
|
background: linear-gradient(180deg, #eaf3ff 0%, #f6faff 100%);
|
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
|
padding-bottom: 40rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.fixed-nav {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
z-index: 100;
|
|
|
|
|
|
background: linear-gradient(180deg, #cbe6ff 0%, #f6faff 100%);
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
|
|
|
|
|
}
|
|
|
|
|
|
.content-area {
|
|
|
|
|
|
padding: 220rpx 24rpx 24rpx 24rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-card {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 16rpx;
|
|
|
|
|
|
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
|
|
|
|
|
|
padding: 32rpx 28rpx;
|
|
|
|
|
|
margin-bottom: 32rpx;
|
|
|
|
|
|
border: 1rpx solid #e5e5e5;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.status-tag {
|
|
|
|
|
|
padding: 6rpx 24rpx;
|
|
|
|
|
|
border-radius: 20rpx;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.status-tag.issued {
|
|
|
|
|
|
background: #2ecc71;
|
|
|
|
|
|
}
|
|
|
|
|
|
.status-tag.pending {
|
|
|
|
|
|
background: #ffa940;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-date {
|
|
|
|
|
|
color: #888;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-title {
|
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-amount {
|
|
|
|
|
|
font-size: 32rpx;
|
|
|
|
|
|
color: #222;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
margin-bottom: 8rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-batch {
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #888;
|
|
|
|
|
|
margin-bottom: 8rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-ship {
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #888;
|
|
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-actions.single-btn .invoice-detail-btn {
|
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
|
width: 153px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-detail-btn {
|
|
|
|
|
|
background: #e4f3fe;
|
|
|
|
|
|
color: #217aff;
|
|
|
|
|
|
height: 69rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
margin-right: 24rpx;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
padding: 8px 0;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-detail-btn.issue {
|
|
|
|
|
|
background: #217aff;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.invoice-btn {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.empty-box {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
margin-top: 120rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.empty-img {
|
|
|
|
|
|
width: 320rpx;
|
|
|
|
|
|
height: 320rpx;
|
|
|
|
|
|
margin-bottom: 32rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.empty-text {
|
|
|
|
|
|
color: #888;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.load-more {
|
|
|
|
|
|
padding: 30rpx 0;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.loading-text {
|
|
|
|
|
|
color: #888;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.no-more-text {
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|