开票历史模块
This commit is contained in:
@@ -335,3 +335,193 @@ export interface InvoiceRequest {
|
||||
export function invoiceIssueApi(payload: InvoiceRequest): Promise<string> {
|
||||
return http.post('/pt/invoiceBlue', payload)
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// 开票历史
|
||||
// =============================================
|
||||
|
||||
/** 分页结果 */
|
||||
export interface PageResult<T> {
|
||||
items: T[]
|
||||
page: number
|
||||
pageSize: number
|
||||
total: number
|
||||
}
|
||||
|
||||
/** 发票历史记录 */
|
||||
export interface InvoiceHistoryItem {
|
||||
id: string
|
||||
/** 发票请求流水号 */
|
||||
invoiceReqSerialNo: string
|
||||
/** 销方税号 */
|
||||
taxpayerNum: string
|
||||
/** 发票种类 */
|
||||
invoiceKindCode: string
|
||||
/** 购买方名称 */
|
||||
buyerName: string
|
||||
/** 购买方税号 */
|
||||
buyerTaxpayerNum?: string
|
||||
/** 购买方地址 */
|
||||
buyerAddress?: string
|
||||
/** 购买方电话 */
|
||||
buyerTel?: string
|
||||
/** 购买方开户银行 */
|
||||
buyerBankName?: string
|
||||
/** 购买方银行账号 */
|
||||
buyerBankAccount?: string
|
||||
/** 不含税金额 */
|
||||
amount: string
|
||||
/** 税额 */
|
||||
taxAmount: string
|
||||
/** 含税总金额 */
|
||||
totalAmount: string
|
||||
/** 发票号码 */
|
||||
invoiceNo?: string
|
||||
/** 发票代码 */
|
||||
invoiceCode?: string
|
||||
/** 数电票号码 */
|
||||
electronicInvoiceNo?: string
|
||||
/** 开票时间 */
|
||||
issuedAt?: string
|
||||
/** 开票状态 */
|
||||
status: string
|
||||
/** PDF 地址 */
|
||||
pdfUrl?: string
|
||||
/** OFD 地址 */
|
||||
ofdUrl?: string
|
||||
/** XML 地址 */
|
||||
xmlUrl?: string
|
||||
/** 订单号 */
|
||||
tradeNo?: string
|
||||
/** 备注 */
|
||||
remark?: string
|
||||
/** 自定义透传数据 */
|
||||
definedData?: string
|
||||
/** 创建时间 */
|
||||
createdAt: string
|
||||
/** 错误信息 */
|
||||
errorMessage?: string
|
||||
}
|
||||
|
||||
/** 发票种类映射 */
|
||||
export const invoiceKindMap: Record<string, string> = {
|
||||
'81': '数电专票',
|
||||
'82': '数电普票',
|
||||
'87': '机动车发票',
|
||||
'10': '电子普票',
|
||||
'08': '电子专票',
|
||||
'04': '增值税普票',
|
||||
'01': '增值税专票'
|
||||
}
|
||||
|
||||
/** 开票状态映射 */
|
||||
export const invoiceStatusMap: Record<string, string> = {
|
||||
'PENDING': '待处理',
|
||||
'PROCESSING': '处理中',
|
||||
'SUCCESS': '开票成功',
|
||||
'FAILED': '开票失败'
|
||||
}
|
||||
|
||||
/** 开票状态颜色映射 */
|
||||
export const invoiceStatusColorMap: Record<string, string> = {
|
||||
'PENDING': '#faad14',
|
||||
'PROCESSING': '#409eff',
|
||||
'SUCCESS': '#52c41a',
|
||||
'FAILED': '#f56c6c'
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询蓝票开票历史
|
||||
*/
|
||||
export function invoiceHistoryApi(page: number, pageSize: number): Promise<PageResult<InvoiceHistoryItem>> {
|
||||
return http.get('/pt/invoiceBlueHistory', { params: { page, pageSize } })
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// 发票详情
|
||||
// =============================================
|
||||
|
||||
/** 发票商品明细(详情) */
|
||||
export interface InvoiceDetailGoods {
|
||||
lineNo: number
|
||||
goodsName: string
|
||||
taxClassificationCode: string
|
||||
specificationModel?: string
|
||||
meteringUnit?: string
|
||||
quantity?: string
|
||||
unitPrice?: string
|
||||
invoiceAmount: string
|
||||
taxRateValue: string
|
||||
taxRateAmount?: string
|
||||
includeTaxFlag: boolean
|
||||
}
|
||||
|
||||
/** 差额征税凭证明细(详情) */
|
||||
export interface InvoiceDetailVoucher {
|
||||
proofType: string
|
||||
electronicInvoiceNo?: string
|
||||
invoiceCode?: string
|
||||
invoiceNo?: string
|
||||
proofNo?: string
|
||||
issueDate?: string
|
||||
proofAmount: string
|
||||
deductionAmount: string
|
||||
proofRemark?: string
|
||||
source?: string
|
||||
}
|
||||
|
||||
/** 关联单据(详情) */
|
||||
export interface InvoiceDetailOrder {
|
||||
orderNo: string
|
||||
}
|
||||
|
||||
/** 发票完整详情 */
|
||||
export interface InvoiceDetailResponse {
|
||||
id: string
|
||||
invoiceReqSerialNo: string
|
||||
taxpayerNum: string
|
||||
invoiceKindCode: string
|
||||
invoiceType: string
|
||||
buyerName: string
|
||||
buyerTaxpayerNum?: string
|
||||
buyerAddress?: string
|
||||
buyerTel?: string
|
||||
buyerBankName?: string
|
||||
buyerBankAccount?: string
|
||||
amount: string
|
||||
taxAmount: string
|
||||
totalAmount: string
|
||||
invoiceNo?: string
|
||||
invoiceCode?: string
|
||||
electronicInvoiceNo?: string
|
||||
issuedAt?: string
|
||||
status: string
|
||||
errorMessage?: string
|
||||
pdfUrl?: string
|
||||
ofdUrl?: string
|
||||
xmlUrl?: string
|
||||
tradeNo?: string
|
||||
remark?: string
|
||||
definedData?: string
|
||||
createdAt: string
|
||||
/** 商品明细 */
|
||||
goodsList: InvoiceDetailGoods[]
|
||||
/** 差额征税凭证明细 */
|
||||
voucherList: InvoiceDetailVoucher[]
|
||||
/** 关联单据 */
|
||||
orderList: InvoiceDetailOrder[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询发票完整详情
|
||||
*/
|
||||
export function invoiceDetailApi(invoiceReqSerialNo: string): Promise<InvoiceDetailResponse> {
|
||||
return http.get('/pt/invoiceDetail', { params: { invoiceReqSerialNo } })
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询并刷新发票状态
|
||||
*/
|
||||
export function queryInvoiceApi(invoiceReqSerialNo: string): Promise<{ invoiceReqSerialNo: string; status: string }> {
|
||||
return http.get('/pt/queryInvoice', { params: { invoiceReqSerialNo } })
|
||||
}
|
||||
|
||||
@@ -1,35 +1,447 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="placeholder">
|
||||
<h2>开票历史</h2>
|
||||
<p>功能开发中,敬请期待</p>
|
||||
<div class="page-header">
|
||||
<h2 class="page-title">开票历史</h2>
|
||||
</div>
|
||||
|
||||
<div class="search-bar">
|
||||
<n-input
|
||||
v-model:value="query.keyword"
|
||||
placeholder="搜索购买方名称 / 流水号 / 发票号码"
|
||||
clearable
|
||||
style="width: 320px"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
<n-button type="primary" @click="handleSearch">查询</n-button>
|
||||
<n-button @click="handleReset">重置</n-button>
|
||||
</div>
|
||||
|
||||
<n-data-table
|
||||
:columns="columns"
|
||||
:data="dataSource"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:bordered="true"
|
||||
:single-line="false"
|
||||
size="small"
|
||||
striped
|
||||
class="history-table"
|
||||
@update:page="handlePageChange"
|
||||
@update:page-size="handlePageSizeChange"
|
||||
/>
|
||||
|
||||
<!-- 查看详情弹窗 -->
|
||||
<n-modal
|
||||
v-model:show="showDetail"
|
||||
preset="card"
|
||||
title="发票详情"
|
||||
style="width: 900px; max-width: 92vw"
|
||||
:mask-closable="false"
|
||||
>
|
||||
<template v-if="detailItem">
|
||||
<n-spin :show="detailLoading">
|
||||
<!-- ===== 基本信息 ===== -->
|
||||
<n-descriptions :column="2" bordered size="small" label-placement="left">
|
||||
<n-descriptions-item label="发票请求流水号" span="2">
|
||||
{{ detailItem.invoiceReqSerialNo }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="销方税号">{{ detailItem.taxpayerNum }}</n-descriptions-item>
|
||||
<n-descriptions-item label="发票种类">{{ invoiceKindMap[detailItem.invoiceKindCode] || detailItem.invoiceKindCode }}</n-descriptions-item>
|
||||
<n-descriptions-item label="购买方名称">{{ detailItem.buyerName }}</n-descriptions-item>
|
||||
<n-descriptions-item label="购买方税号">{{ detailItem.buyerTaxpayerNum || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="购买方地址">{{ detailItem.buyerAddress || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="购买方电话">{{ detailItem.buyerTel || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="购买方开户银行">{{ detailItem.buyerBankName || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="购买方银行账号">{{ detailItem.buyerBankAccount || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="不含税金额">{{ detailItem.amount }}</n-descriptions-item>
|
||||
<n-descriptions-item label="税额">{{ detailItem.taxAmount }}</n-descriptions-item>
|
||||
<n-descriptions-item label="含税总金额">
|
||||
<span style="font-weight: 600; color: #d4380d">{{ detailItem.totalAmount }}</span>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="发票号码">{{ detailItem.invoiceNo || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="发票代码">{{ detailItem.invoiceCode || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="数电票号码">{{ detailItem.electronicInvoiceNo || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="开票时间">{{ detailItem.issuedAt || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="状态">
|
||||
<n-tag :type="statusTagType(detailItem.status)" size="small">
|
||||
{{ invoiceStatusMap[detailItem.status] || detailItem.status }}
|
||||
</n-tag>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="订单号">{{ detailItem.tradeNo || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item label="备注" span="2">{{ detailItem.remark || '-' }}</n-descriptions-item>
|
||||
<n-descriptions-item v-if="detailItem.errorMessage" label="错误信息" span="2">
|
||||
<span style="color: #f56c6c">{{ detailItem.errorMessage }}</span>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="创建时间">{{ detailItem.createdAt }}</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
|
||||
<!-- 文件下载 -->
|
||||
<div v-if="detailItem.pdfUrl || detailItem.ofdUrl || detailItem.xmlUrl" class="file-links">
|
||||
<span class="file-links-label">文件下载:</span>
|
||||
<n-button v-if="detailItem.pdfUrl" text tag="a" :href="detailItem.pdfUrl" target="_blank" type="primary">PDF</n-button>
|
||||
<n-button v-if="detailItem.ofdUrl" text tag="a" :href="detailItem.ofdUrl" target="_blank" type="primary">OFD</n-button>
|
||||
<n-button v-if="detailItem.xmlUrl" text tag="a" :href="detailItem.xmlUrl" target="_blank" type="primary">XML</n-button>
|
||||
</div>
|
||||
|
||||
<!-- ===== 商品明细 ===== -->
|
||||
<div v-if="detailItem.goodsList.length > 0" class="detail-section">
|
||||
<div class="detail-section-title">商品明细</div>
|
||||
<n-data-table
|
||||
:data="detailItem.goodsList"
|
||||
:columns="goodsColumns"
|
||||
:bordered="true"
|
||||
:single-line="false"
|
||||
size="small"
|
||||
striped
|
||||
:pagination="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- ===== 差额征税凭证明细 ===== -->
|
||||
<div v-if="detailItem.voucherList.length > 0" class="detail-section">
|
||||
<div class="detail-section-title">差额征税凭证明细</div>
|
||||
<n-data-table
|
||||
:data="detailItem.voucherList"
|
||||
:columns="voucherColumns"
|
||||
:bordered="true"
|
||||
:single-line="false"
|
||||
size="small"
|
||||
striped
|
||||
:pagination="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- ===== 关联单据 ===== -->
|
||||
<div v-if="detailItem.orderList.length > 0" class="detail-section">
|
||||
<div class="detail-section-title">关联单据</div>
|
||||
<n-tag v-for="(ord, idx) in detailItem.orderList" :key="idx" style="margin-right: 8px; margin-bottom: 4px">
|
||||
{{ ord.orderNo }}
|
||||
</n-tag>
|
||||
</div>
|
||||
</n-spin>
|
||||
</template>
|
||||
</n-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, onMounted, reactive, ref } from 'vue'
|
||||
import {
|
||||
NButton,
|
||||
NDataTable,
|
||||
NDescriptions,
|
||||
NDescriptionsItem,
|
||||
NInput,
|
||||
NModal,
|
||||
NSpin,
|
||||
NTag,
|
||||
useMessage
|
||||
} from 'naive-ui'
|
||||
import { Eye, RefreshCw } from 'lucide-vue-next'
|
||||
import { invoiceDetailApi, invoiceHistoryApi, queryInvoiceApi } from '@/api/piaotong'
|
||||
import type { InvoiceDetailGoods, InvoiceDetailOrder, InvoiceDetailResponse, InvoiceDetailVoucher, InvoiceHistoryItem } from '@/api/piaotong'
|
||||
import type { DataTableColumn } from 'naive-ui'
|
||||
|
||||
const invoiceKindMap: Record<string, string> = {
|
||||
'81': '数电专票',
|
||||
'82': '数电普票',
|
||||
'87': '机动车发票',
|
||||
'10': '电子普票',
|
||||
'08': '电子专票',
|
||||
'04': '增值税普票',
|
||||
'01': '增值税专票'
|
||||
}
|
||||
|
||||
const invoiceStatusMap: Record<string, string> = {
|
||||
'PENDING': '待处理',
|
||||
'PROCESSING': '处理中',
|
||||
'SUCCESS': '开票成功',
|
||||
'FAILED': '开票失败'
|
||||
}
|
||||
|
||||
function statusTagType(status: string): 'warning' | 'info' | 'success' | 'error' {
|
||||
switch (status) {
|
||||
case 'PENDING': return 'warning'
|
||||
case 'PROCESSING': return 'info'
|
||||
case 'SUCCESS': return 'success'
|
||||
case 'FAILED': return 'error'
|
||||
default: return 'info'
|
||||
}
|
||||
}
|
||||
|
||||
const message = useMessage()
|
||||
const loading = ref(false)
|
||||
const dataSource = ref<InvoiceHistoryItem[]>([])
|
||||
const showDetail = ref(false)
|
||||
const detailLoading = ref(false)
|
||||
const detailItem = ref<InvoiceDetailResponse | null>(null)
|
||||
/** 记录正在刷新状态的流水号 */
|
||||
const refreshingSet = reactive(new Set<string>())
|
||||
|
||||
const query = reactive({
|
||||
keyword: ''
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
pageCount: 1,
|
||||
itemCount: 0,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
pageSlot: 7,
|
||||
prefix: ({ itemCount }: { itemCount: number }) => `共 ${itemCount} 条`
|
||||
})
|
||||
|
||||
/** 商品明细表格列 */
|
||||
const goodsColumns: DataTableColumn[] = [
|
||||
{ title: '行号', key: 'lineNo', width: 60, align: 'center' },
|
||||
{ title: '商品名称', key: 'goodsName', width: 160, ellipsis: { tooltip: true } },
|
||||
{ title: '税收分类编码', key: 'taxClassificationCode', width: 130, ellipsis: { tooltip: true } },
|
||||
{ title: '规格型号', key: 'specificationModel', width: 100, render: (r: InvoiceDetailGoods) => r.specificationModel || '-' },
|
||||
{ title: '单位', key: 'meteringUnit', width: 60, render: (r: InvoiceDetailGoods) => r.meteringUnit || '-' },
|
||||
{ title: '数量', key: 'quantity', width: 80, render: (r: InvoiceDetailGoods) => r.quantity || '-' },
|
||||
{ title: '单价', key: 'unitPrice', width: 100, render: (r: InvoiceDetailGoods) => r.unitPrice || '-' },
|
||||
{ title: '金额', key: 'invoiceAmount', width: 100, align: 'right' },
|
||||
{ title: '税率', key: 'taxRateValue', width: 70, render: (r: InvoiceDetailGoods) => `${(parseFloat(r.taxRateValue) * 100).toFixed(0)}%` },
|
||||
{ title: '税额', key: 'taxRateAmount', width: 100, align: 'right', render: (r: InvoiceDetailGoods) => r.taxRateAmount || '-' },
|
||||
{ title: '含税', key: 'includeTaxFlag', width: 60, align: 'center', render: (r: InvoiceDetailGoods) => r.includeTaxFlag ? '是' : '否' },
|
||||
]
|
||||
|
||||
/** 差额征税凭证表格列 */
|
||||
const voucherColumns: DataTableColumn[] = [
|
||||
{ title: '凭证类型', key: 'proofType', width: 120, render: (r: InvoiceDetailVoucher) => proofTypeMap[r.proofType] || r.proofType },
|
||||
{ title: '凭证号码', key: 'proofNo', width: 130, render: (r: InvoiceDetailVoucher) => r.proofNo || '-' },
|
||||
{ title: '开具日期', key: 'issueDate', width: 100, render: (r: InvoiceDetailVoucher) => r.issueDate || '-' },
|
||||
{ title: '凭证金额', key: 'proofAmount', width: 110, align: 'right' },
|
||||
{ title: '扣除金额', key: 'deductionAmount', width: 110, align: 'right' },
|
||||
{ title: '来源', key: 'source', width: 90, render: (r: InvoiceDetailVoucher) => r.source || '-' },
|
||||
]
|
||||
|
||||
const proofTypeMap: Record<string, string> = {
|
||||
'01': '数电票', '02': '增值税专票', '03': '增值税普票', '04': '营业税发票',
|
||||
'05': '财政票据', '06': '法院裁决书', '07': '契税完税凭证', '08': '其他发票类', '09': '其他扣除凭证'
|
||||
}
|
||||
|
||||
const columns: DataTableColumn[] = [
|
||||
{
|
||||
title: '流水号',
|
||||
key: 'invoiceReqSerialNo',
|
||||
width: 180,
|
||||
ellipsis: { tooltip: true }
|
||||
},
|
||||
{
|
||||
title: '购买方',
|
||||
key: 'buyerName',
|
||||
width: 140,
|
||||
ellipsis: { tooltip: true }
|
||||
},
|
||||
{
|
||||
title: '发票种类',
|
||||
key: 'invoiceKindCode',
|
||||
width: 100,
|
||||
render: (row: InvoiceHistoryItem) => invoiceKindMap[row.invoiceKindCode] || row.invoiceKindCode
|
||||
},
|
||||
{
|
||||
title: '不含税金额',
|
||||
key: 'amount',
|
||||
width: 120,
|
||||
align: 'right'
|
||||
},
|
||||
{
|
||||
title: '税额',
|
||||
key: 'taxAmount',
|
||||
width: 110,
|
||||
align: 'right'
|
||||
},
|
||||
{
|
||||
title: '含税总金额',
|
||||
key: 'totalAmount',
|
||||
width: 130,
|
||||
align: 'right',
|
||||
sorter: (a: InvoiceHistoryItem, b: InvoiceHistoryItem) =>
|
||||
parseFloat(a.totalAmount) - parseFloat(b.totalAmount),
|
||||
render: (row: InvoiceHistoryItem) =>
|
||||
h('span', { style: { fontWeight: 600, color: '#d4380d' } }, row.totalAmount)
|
||||
},
|
||||
{
|
||||
title: '发票号码',
|
||||
key: 'invoiceNo',
|
||||
width: 120,
|
||||
ellipsis: { tooltip: true },
|
||||
render: (row: InvoiceHistoryItem) => row.invoiceNo || '-'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: (row: InvoiceHistoryItem) =>
|
||||
h(NTag, { type: statusTagType(row.status), size: 'small' }, () =>
|
||||
invoiceStatusMap[row.status] || row.status
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '开票时间',
|
||||
key: 'issuedAt',
|
||||
width: 140,
|
||||
render: (row: InvoiceHistoryItem) => row.issuedAt || '-'
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createdAt',
|
||||
width: 140
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
render: (row: InvoiceHistoryItem) =>
|
||||
h('div', { style: 'display: flex; gap: 8px; align-items: center;' }, [
|
||||
h(
|
||||
NButton,
|
||||
{ size: 'small', type: 'primary', tertiary: true, onClick: () => showDetailInfo(row) },
|
||||
{ default: () => '查看详情' }
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'warning',
|
||||
secondary: true,
|
||||
disabled: refreshingSet.has(row.invoiceReqSerialNo),
|
||||
onClick: () => refreshStatus(row)
|
||||
},
|
||||
{ default: () => refreshingSet.has(row.invoiceReqSerialNo) ? '刷新中...' : '刷新状态', icon: () => h(RefreshCw, { size: 14 }) }
|
||||
)
|
||||
])
|
||||
}
|
||||
]
|
||||
|
||||
async function fetchData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await invoiceHistoryApi(pagination.page, pagination.pageSize)
|
||||
dataSource.value = res.items
|
||||
pagination.itemCount = res.total
|
||||
pagination.pageCount = Math.max(1, Math.ceil(res.total / pagination.pageSize))
|
||||
} catch {
|
||||
message.error('查询开票历史失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
pagination.page = page
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handlePageSizeChange(pageSize: number) {
|
||||
pagination.pageSize = pageSize
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
query.keyword = ''
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
async function showDetailInfo(item: InvoiceHistoryItem) {
|
||||
showDetail.value = true
|
||||
detailLoading.value = true
|
||||
try {
|
||||
const res = await invoiceDetailApi(item.invoiceReqSerialNo)
|
||||
detailItem.value = res
|
||||
} catch {
|
||||
detailItem.value = null
|
||||
useMessage().error('查询发票详情失败')
|
||||
} finally {
|
||||
detailLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshStatus(item: InvoiceHistoryItem) {
|
||||
refreshingSet.add(item.invoiceReqSerialNo)
|
||||
try {
|
||||
const res = await queryInvoiceApi(item.invoiceReqSerialNo)
|
||||
message.success(`状态已刷新: ${invoiceStatusMap[res.status] || res.status}`)
|
||||
// 重新加载列表数据更新此行状态
|
||||
await fetchData()
|
||||
} catch {
|
||||
message.error('刷新状态失败')
|
||||
} finally {
|
||||
refreshingSet.delete(item.invoiceReqSerialNo)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
min-height: 100%;
|
||||
background: #f7f8fa;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
text-align: center;
|
||||
color: #bbb;
|
||||
.page-header {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.placeholder h2 {
|
||||
margin: 0 0 8px;
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #999;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.placeholder p {
|
||||
margin: 0;
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.history-table {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.file-links {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.file-links-label {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.detail-section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid #409eff;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -448,13 +448,13 @@
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="金额 *" path="invoiceAmount">
|
||||
<n-input v-model:value="currentItem.invoiceAmount" :placeholder="amountPlaceholder" clearable />
|
||||
<n-form-item label="金额是否含税 *" path="includeTaxFlag">
|
||||
<n-select v-model:value="currentItem.includeTaxFlag" :options="includeTaxFlagOptions" placeholder="选择含税标示" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="含税标示 *" path="includeTaxFlag">
|
||||
<n-select v-model:value="currentItem.includeTaxFlag" :options="includeTaxFlagOptions" placeholder="选择含税标示" />
|
||||
<n-form-item label="金额 *" path="invoiceAmount">
|
||||
<n-input v-model:value="currentItem.invoiceAmount" :placeholder="amountPlaceholder" clearable />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
@@ -538,18 +538,18 @@
|
||||
</n-form>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- ===== 新增单据号弹窗 ===== -->
|
||||
<n-modal v-model:show="showOrderDialog" preset="card" title="新增单据号" style="width:420px" :mask-closable="false">
|
||||
<n-input v-model:value="orderInputValue" placeholder="请输入业务单据号" clearable @keyup.enter="confirmAddOrderNo" />
|
||||
<template #footer>
|
||||
<div style="display:flex;justify-content:flex-end;gap:8px">
|
||||
<n-button @click="showOrderDialog = false">取消</n-button>
|
||||
<n-button type="primary" @click="confirmAddOrderNo">确定</n-button>
|
||||
</div>
|
||||
</template>
|
||||
</n-modal>
|
||||
<!-- ===== 新增单据号弹窗 ===== -->
|
||||
<n-modal v-model:show="showOrderDialog" preset="card" title="新增单据号" style="width:420px" :mask-closable="false">
|
||||
<n-input v-model:value="orderInputValue" placeholder="请输入业务单据号" clearable @keyup.enter="confirmAddOrderNo" />
|
||||
<template #footer>
|
||||
<div style="display:flex;justify-content:flex-end;gap:8px">
|
||||
<n-button @click="showOrderDialog = false">取消</n-button>
|
||||
<n-button type="primary" @click="confirmAddOrderNo">确定</n-button>
|
||||
</div>
|
||||
</template>
|
||||
</n-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -557,7 +557,6 @@ import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { Plus, RefreshCw } from 'lucide-vue-next'
|
||||
import {
|
||||
NButton,
|
||||
NCard,
|
||||
NDatePicker,
|
||||
NForm,
|
||||
NFormItem,
|
||||
@@ -1111,19 +1110,24 @@ watch(() => form.specialInvoiceKind, (newVal, oldVal) => {
|
||||
if (!newVal || newVal === oldVal) return
|
||||
|
||||
if (newVal === '02') {
|
||||
dialog.warning({
|
||||
title: '提示',
|
||||
content: '农产品收购发票的开票种类只能是数电普票(82),是否将开票种类修改为"数电普票(82)"?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
form.invoiceIssueKindCode = '82'
|
||||
showSwapSellerBuyerDialog()
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
form.specialInvoiceKind = oldVal
|
||||
}
|
||||
})
|
||||
if (form.invoiceIssueKindCode === '82') {
|
||||
// 已经是数电普票,直接弹交换信息弹窗
|
||||
showSwapSellerBuyerDialog()
|
||||
} else {
|
||||
dialog.warning({
|
||||
title: '提示',
|
||||
content: '农产品收购发票的开票种类只能是数电普票(82),是否将开票种类修改为"数电普票(82)"?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
form.invoiceIssueKindCode = '82'
|
||||
showSwapSellerBuyerDialog()
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
form.specialInvoiceKind = oldVal
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if (newVal === '12') {
|
||||
showSwapSellerBuyerDialog()
|
||||
}
|
||||
@@ -1237,7 +1241,7 @@ watch(() => form.itemList, (items) => {
|
||||
flex-shrink: 0;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
padding: 12px 24px;
|
||||
padding: 6px 24px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||
|
||||
Reference in New Issue
Block a user