feat:账单系统
This commit is contained in:
@@ -61,7 +61,7 @@ function createCommonRequest<ResponseData = any>(
|
|||||||
return Promise.resolve(response);
|
return Promise.resolve(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.isBackendSuccess(response)) {
|
if (opts.isBackendSuccess(response) || response.config?.responseType === "blob") {
|
||||||
return Promise.resolve(response);
|
return Promise.resolve(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const viewEn: any = {
|
|||||||
"view.endpoint_access": "Current Connection",
|
"view.endpoint_access": "Current Connection",
|
||||||
"view.endpoint_records": "Past Connection",
|
"view.endpoint_records": "Past Connection",
|
||||||
"view.endpoint_cdrlrecords":"Internet Records",
|
"view.endpoint_cdrlrecords":"Internet Records",
|
||||||
|
"view.endpoint_bill":"Bill Records",
|
||||||
"view.billing": "Billing",
|
"view.billing": "Billing",
|
||||||
"view.billing_histories": "Historical",
|
"view.billing_histories": "Historical",
|
||||||
"view.billing_Rechargehistory":"Recharge History",
|
"view.billing_Rechargehistory":"Recharge History",
|
||||||
@@ -740,6 +741,30 @@ const local: any = {
|
|||||||
confirm:'Confirm',
|
confirm:'Confirm',
|
||||||
insufficientBalance:'Insufficient balance'
|
insufficientBalance:'Insufficient balance'
|
||||||
},
|
},
|
||||||
|
bill:{
|
||||||
|
loading:'Loading',
|
||||||
|
tipTitle:'Bill DownLoad',
|
||||||
|
exportTip:'Sure to download?',
|
||||||
|
total:'Total',
|
||||||
|
invoiceNumber:'Bill Number',
|
||||||
|
title: 'Bill List',
|
||||||
|
billDate: 'Bill Date',
|
||||||
|
amount: 'Amount',
|
||||||
|
billType: 'Bill Type',
|
||||||
|
statu: 'Status',
|
||||||
|
actions: 'Action',
|
||||||
|
download: 'Download',
|
||||||
|
preview: 'Preview',
|
||||||
|
exportOk: 'Download Success',
|
||||||
|
exportFail: 'Download Fail',
|
||||||
|
previewTitle: 'Bill Preview',
|
||||||
|
previewFailed: 'Preview fail',
|
||||||
|
noFile: 'No billing documents available',
|
||||||
|
types: {
|
||||||
|
recharge:'Recharge',
|
||||||
|
package: 'Package',
|
||||||
|
},
|
||||||
|
},
|
||||||
kyc:{
|
kyc:{
|
||||||
rejectReason:'Reject Reason:',
|
rejectReason:'Reject Reason:',
|
||||||
drive:'Driving license',
|
drive:'Driving license',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const viewZh: any = {
|
|||||||
"view.endpoint_access": "当前设备",
|
"view.endpoint_access": "当前设备",
|
||||||
"view.endpoint_records": "历史设备",
|
"view.endpoint_records": "历史设备",
|
||||||
"view.endpoint_cdrlrecords":"上网记录",
|
"view.endpoint_cdrlrecords":"上网记录",
|
||||||
|
"view.endpoint_bill":"账单记录",
|
||||||
"view.billing": "账单",
|
"view.billing": "账单",
|
||||||
"view.billing_billservice":"账单服务",
|
"view.billing_billservice":"账单服务",
|
||||||
"view.billing_histories": "历史查询",
|
"view.billing_histories": "历史查询",
|
||||||
@@ -602,6 +603,36 @@ const local:any = {
|
|||||||
Paid:'已支付',
|
Paid:'已支付',
|
||||||
Unpaid:'未支付',
|
Unpaid:'未支付',
|
||||||
},
|
},
|
||||||
|
bill:{
|
||||||
|
loading:'加载中',
|
||||||
|
tipTitle:'账单下载',
|
||||||
|
exportTip:'确定下载吗?',
|
||||||
|
total:'共',
|
||||||
|
invoiceNumber:'账单编号',
|
||||||
|
title: '账单列表',
|
||||||
|
billDate: '账单日期',
|
||||||
|
amount: '金额',
|
||||||
|
billType: '账单类型',
|
||||||
|
statu: '状态',
|
||||||
|
actions: '操作',
|
||||||
|
download: '下载',
|
||||||
|
preview: '预览',
|
||||||
|
status: {
|
||||||
|
pending: '待支付',
|
||||||
|
paid: '已支付',
|
||||||
|
failed: '支付失败',
|
||||||
|
unknown: '未知状态'
|
||||||
|
},
|
||||||
|
exportOk: '下载成功',
|
||||||
|
exportFail: '下载失败',
|
||||||
|
previewTitle: '账单预览',
|
||||||
|
previewFailed: '预览失败',
|
||||||
|
noFile: '暂无账单文件',
|
||||||
|
types: {
|
||||||
|
recharge:'余额充值',
|
||||||
|
package: '套餐费用',
|
||||||
|
},
|
||||||
|
},
|
||||||
Internetdetails:{
|
Internetdetails:{
|
||||||
title:"上网详单",
|
title:"上网详单",
|
||||||
month:"月",
|
month:"月",
|
||||||
|
|||||||
@@ -415,6 +415,17 @@ export const customRoutes: GeneratedRoute[] = [
|
|||||||
order: 4
|
order: 4
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'billing_bill',
|
||||||
|
path: '/billing/bill',
|
||||||
|
component: 'view.billing_bill',
|
||||||
|
meta: {
|
||||||
|
title: '上网记录',
|
||||||
|
i18nKey: 'view.endpoint_bill',
|
||||||
|
icon: 'ant-design:book-outlined',
|
||||||
|
order: 5
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'billing_wxpay',
|
name: 'billing_wxpay',
|
||||||
path: '/billing/wxpay',
|
path: '/billing/wxpay',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { request } from '../request';
|
|
||||||
|
|
||||||
|
import { request, rawRequest } from '../request';
|
||||||
/**
|
/**
|
||||||
* Login
|
* Login
|
||||||
*
|
*
|
||||||
@@ -206,6 +206,15 @@ export function resetPasswordByEmail(data: { email: string; code: string; passwo
|
|||||||
data
|
data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/** 下载账单 PDF 文件 */
|
||||||
|
/** 下载账单 PDF 文件(使用 axios 直连) */
|
||||||
|
/** 下载账单 PDF 文件(使用 rawRequest,避免 transformBackendResponse 破坏 blob) */
|
||||||
|
export function downloadBill(id: string) {
|
||||||
|
return request({
|
||||||
|
url: `/u/bill/download/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
21
src/typings/api.d.ts
vendored
21
src/typings/api.d.ts
vendored
@@ -676,13 +676,20 @@ declare namespace Api {
|
|||||||
namespace Bill {
|
namespace Bill {
|
||||||
/** Bill record information */
|
/** Bill record information */
|
||||||
interface BillRecord {
|
interface BillRecord {
|
||||||
id: string;
|
/** 账单编号 */
|
||||||
startTime: string | null;
|
invoiceNumber: string;
|
||||||
endTime: string | null;
|
/** 账单金额 */
|
||||||
traffic: string | null;
|
amount: number;
|
||||||
amount: string;
|
/** 账单类型:0-购买套餐,1-余额充值 */
|
||||||
status: number;
|
type: 0 | 1;
|
||||||
createTime: string;
|
/** 账单时间 */
|
||||||
|
invoiceTime: string;
|
||||||
|
/** 账单状态:0-待支付,1-已支付,2-支付失败 */
|
||||||
|
status: 0 | 1 | 2;
|
||||||
|
/** 账单文件URL */
|
||||||
|
fileUrl?: string;
|
||||||
|
/** 发票文件URL */
|
||||||
|
invoiceFile?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Bill list response */
|
/** Bill list response */
|
||||||
|
|||||||
3
src/typings/components.d.ts
vendored
3
src/typings/components.d.ts
vendored
@@ -34,14 +34,12 @@ declare module 'vue' {
|
|||||||
AMenu: typeof import('ant-design-vue/es')['Menu']
|
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||||
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
||||||
AModal: typeof import('ant-design-vue/es')['Modal']
|
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||||
APagination: typeof import('ant-design-vue/es')['Pagination']
|
|
||||||
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||||
APopover: typeof import('ant-design-vue/es')['Popover']
|
APopover: typeof import('ant-design-vue/es')['Popover']
|
||||||
AppLoading: typeof import('./../components/common/app-loading.vue')['default']
|
AppLoading: typeof import('./../components/common/app-loading.vue')['default']
|
||||||
AppProvider: typeof import('./../components/common/app-provider.vue')['default']
|
AppProvider: typeof import('./../components/common/app-provider.vue')['default']
|
||||||
ARadio: typeof import('ant-design-vue/es')['Radio']
|
ARadio: typeof import('ant-design-vue/es')['Radio']
|
||||||
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||||
ARangePicker: typeof import('ant-design-vue/es')['RangePicker']
|
|
||||||
ARow: typeof import('ant-design-vue/es')['Row']
|
ARow: typeof import('ant-design-vue/es')['Row']
|
||||||
ASegmented: typeof import('ant-design-vue/es')['Segmented']
|
ASegmented: typeof import('ant-design-vue/es')['Segmented']
|
||||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||||
@@ -80,7 +78,6 @@ declare module 'vue' {
|
|||||||
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||||
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
||||||
OrderConfirmModal: typeof import('./../components/order-confirm/orderConfirmModal.vue')['default']
|
OrderConfirmModal: typeof import('./../components/order-confirm/orderConfirmModal.vue')['default']
|
||||||
Pay: typeof import('./../components/pay/pay.vue')['default']
|
|
||||||
PaypalButton: typeof import('./../components/payment/paypal-button.vue')['default']
|
PaypalButton: typeof import('./../components/payment/paypal-button.vue')['default']
|
||||||
PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
|
PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
|
||||||
ReloadButton: typeof import('./../components/common/reload-button.vue')['default']
|
ReloadButton: typeof import('./../components/common/reload-button.vue')['default']
|
||||||
|
|||||||
337
src/views/billing/bill/index.vue
Normal file
337
src/views/billing/bill/index.vue
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, onMounted, h } from 'vue'
|
||||||
|
import type { TableColumnsType } from 'ant-design-vue'
|
||||||
|
import { Table as ATable, Button as AButton, Modal, message } from 'ant-design-vue'
|
||||||
|
import { useI18n } from "vue-i18n"
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useWindowSize } from '@vueuse/core'
|
||||||
|
import { useTable } from '@/hooks/common/table'
|
||||||
|
import { fetchBillHistory, downloadBill } from '@/service/api/auth'
|
||||||
|
import { getPaymentConfig } from '@/service/api/payment'
|
||||||
|
import type { Api } from '@/typings/api'
|
||||||
|
import { SimpleScrollbar } from '~/packages/materials/src'
|
||||||
|
import { Card as ACard, Tag as ATag } from 'ant-design-vue'
|
||||||
|
import { useElementSize } from '@vueuse/core'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const { t } = useI18n()
|
||||||
|
const currencySymbol = ref('¥')
|
||||||
|
|
||||||
|
// 获取货币符号
|
||||||
|
const fetchCurrencySymbol = async () => {
|
||||||
|
try {
|
||||||
|
const response = await getPaymentConfig()
|
||||||
|
if (response && response.data) {
|
||||||
|
currencySymbol.value = response.data.currencySymbol
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// console.error('Failed to fetch currency symbol:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期时间
|
||||||
|
const formatDateTime = (dateTimeString: string) => {
|
||||||
|
if (!dateTimeString) return ''
|
||||||
|
const date = new Date(dateTimeString)
|
||||||
|
return date.toLocaleString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式布局
|
||||||
|
const { width } = useWindowSize()
|
||||||
|
const isMobile = computed(() => width.value < 768)
|
||||||
|
|
||||||
|
// 处理文件URL
|
||||||
|
const parseFile = (path: string) => {
|
||||||
|
let baseUrl = import.meta.env.VITE_SERVICE_BASE_URL;
|
||||||
|
// console.log(baseUrl)
|
||||||
|
// debugger
|
||||||
|
|
||||||
|
// 移除末尾的斜杠
|
||||||
|
baseUrl = baseUrl.replace(/\/+$/, '');
|
||||||
|
|
||||||
|
// 从完整 URL 中提取域名部分
|
||||||
|
if (baseUrl.includes('://')) {
|
||||||
|
try {
|
||||||
|
const url = new URL(baseUrl);
|
||||||
|
baseUrl = url.host; // 这将获取域名和端口(如果有的话)
|
||||||
|
} catch (error) {
|
||||||
|
// 如果解析失败,使用原始值的域名部分
|
||||||
|
baseUrl = baseUrl.replace(/^https?:\/\//, '').split('/')[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造最终的 URL
|
||||||
|
return `${path.startsWith('/') ? path : '/' + path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapperEl = ref<HTMLElement | null>(null)
|
||||||
|
const { height: wrapperElHeight } = useElementSize(wrapperEl)
|
||||||
|
|
||||||
|
const scrollConfig = computed(() => ({
|
||||||
|
y: wrapperElHeight.value - 72,
|
||||||
|
x: 800
|
||||||
|
}))
|
||||||
|
|
||||||
|
const getStatusColor = (status: number) => {
|
||||||
|
switch (status) {
|
||||||
|
case 0:
|
||||||
|
return 'warning'
|
||||||
|
case 1:
|
||||||
|
return 'success'
|
||||||
|
case 2:
|
||||||
|
return 'error'
|
||||||
|
default:
|
||||||
|
return 'default'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusText = (status: number) => {
|
||||||
|
switch (status) {
|
||||||
|
case 0:
|
||||||
|
return t('page.bill.status.pending')
|
||||||
|
case 1:
|
||||||
|
return t('page.bill.status.paid')
|
||||||
|
case 2:
|
||||||
|
return t('page.bill.status.failed')
|
||||||
|
default:
|
||||||
|
return t('page.bill.status.unknown')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格相关配置
|
||||||
|
const { columns, data, loading, getData, mobilePagination, searchParams } = useTable<Api.Bill.BillRecord>({
|
||||||
|
apiFn: fetchBillHistory,
|
||||||
|
apiParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10
|
||||||
|
},
|
||||||
|
rowKey: 'id',
|
||||||
|
pagination: true,
|
||||||
|
columns: (): AntDesign.TableColumn<Api.Bill.BillRecord>[] => [
|
||||||
|
{
|
||||||
|
key: 'invoiceNumber',
|
||||||
|
dataIndex: 'invoiceNumber',
|
||||||
|
title: t('page.bill.invoiceNumber'),
|
||||||
|
align: 'center',
|
||||||
|
width: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'invoiceTime',
|
||||||
|
dataIndex: 'invoiceTime',
|
||||||
|
title: t('page.bill.billDate'),
|
||||||
|
align: 'center',
|
||||||
|
width: 150,
|
||||||
|
customRender: ({ text }: { text: string }) => text ? formatDateTime(text) : ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'amount',
|
||||||
|
dataIndex: 'amount',
|
||||||
|
title: t('page.bill.amount'),
|
||||||
|
align: 'center',
|
||||||
|
width: 120,
|
||||||
|
customRender: ({ text }: { text: number }) => {
|
||||||
|
const formattedAmount = Number(text).toFixed(2)
|
||||||
|
return `${currencySymbol.value}${formattedAmount}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'type',
|
||||||
|
dataIndex: 'type',
|
||||||
|
title: t('page.bill.billType'),
|
||||||
|
align: 'center',
|
||||||
|
width: 120,
|
||||||
|
customRender: ({ text }: { text: Api.Bill.BillRecord['type'] }) => {
|
||||||
|
const typeMap: Record<Api.Bill.BillRecord['type'],{ color: string; label: string }> = {
|
||||||
|
0: { color: 'blue', label: t('page.bill.types.package') },
|
||||||
|
1: { color: 'gold', label: t('page.bill.types.recharge') }
|
||||||
|
}
|
||||||
|
const typeInfo = typeMap[text] || { color: 'default', label: t('page.bill.types.unknown') }
|
||||||
|
return h(ATag, { color: typeInfo.color }, () => typeInfo.label)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'invoiceFile',
|
||||||
|
title: t('page.bill.actions'),
|
||||||
|
align: 'center',
|
||||||
|
width: 150,
|
||||||
|
fixed: 'right'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 下载账单
|
||||||
|
const handleExport = (record: Api.Bill.BillRecord) => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('page.bill.tipTitle'),
|
||||||
|
content: t('page.bill.exportTip'),
|
||||||
|
async onOk() {
|
||||||
|
const key = 'exportJob';
|
||||||
|
message.loading({ content: t('page.bill.loading'), key });
|
||||||
|
try {
|
||||||
|
const res = await downloadBill(record.id);
|
||||||
|
// console.log('downloadBill 返回:', res);
|
||||||
|
let blob;
|
||||||
|
if (res instanceof Blob) {
|
||||||
|
blob = res;
|
||||||
|
} else if (res.data instanceof Blob) {
|
||||||
|
blob = res.data;
|
||||||
|
} else {
|
||||||
|
blob = new Blob([res.data]);
|
||||||
|
}
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
link.download = `invoice-${record.invoiceNumber || record.id}.pdf`;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
message.success({ content: t('page.bill.exportOk'), key, duration: 2 });
|
||||||
|
} catch (e) {
|
||||||
|
// console.error('导出异常:', e);
|
||||||
|
message.error({ content: t('page.bill.exportFail'), key, duration: 2 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览账单
|
||||||
|
const handlePreview = async (record: Api.Bill.BillRecord) => {
|
||||||
|
// console.log('handlePreview被调用', record);
|
||||||
|
if (!record.invoiceFile) {
|
||||||
|
message.error(t('page.bill.noFile'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const url = parseFile(record.invoiceFile)
|
||||||
|
// console.log('预览PDF地址:', url, record.invoiceFile)
|
||||||
|
Modal.info({
|
||||||
|
title: t('page.bill.previewTitle'),
|
||||||
|
width: '80%',
|
||||||
|
content: h('iframe', {
|
||||||
|
src: url,
|
||||||
|
style: {
|
||||||
|
width: '100%',
|
||||||
|
height: '80vh',
|
||||||
|
border: 'none'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
message.error(t('page.bill.previewFailed'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchCurrencySymbol()
|
||||||
|
getData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SimpleScrollbar>
|
||||||
|
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||||
|
<ACard
|
||||||
|
:title="t('page.bill.title')"
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ flex: 1, overflow: 'hidden' }"
|
||||||
|
class="flex-col-stretch sm:flex-1-hidden card-wrapper"
|
||||||
|
>
|
||||||
|
<ATable
|
||||||
|
ref="wrapperEl"
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="data"
|
||||||
|
:loading="loading"
|
||||||
|
row-key="id"
|
||||||
|
size="small"
|
||||||
|
:pagination="{
|
||||||
|
...mobilePagination,
|
||||||
|
total: mobilePagination.total,
|
||||||
|
current: searchParams.pageNum,
|
||||||
|
pageSize: searchParams.pageSize,
|
||||||
|
showTotal: (total: number) => `${t('page.bill.total')} ${total} `
|
||||||
|
}"
|
||||||
|
:scroll="scrollConfig"
|
||||||
|
class="h-full"
|
||||||
|
@change="(pagination) => {
|
||||||
|
searchParams.pageNum = pagination.current;
|
||||||
|
searchParams.pageSize = pagination.pageSize;
|
||||||
|
getData();
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'status'">
|
||||||
|
<ATag :color="getStatusColor(record.status)">
|
||||||
|
{{ getStatusText(record.status) }}
|
||||||
|
</ATag>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'invoiceFile'">
|
||||||
|
<div class="table-file-wrapper">
|
||||||
|
<AButton
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
:disabled="!record.invoiceFile"
|
||||||
|
@click="handlePreview(record)"
|
||||||
|
>
|
||||||
|
{{ t('page.bill.preview') }}
|
||||||
|
</AButton>
|
||||||
|
<AButton
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
:disabled="!record.invoiceFile"
|
||||||
|
@click="handleExport(record)"
|
||||||
|
>
|
||||||
|
{{ t('page.bill.download') }}
|
||||||
|
</AButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</ATable>
|
||||||
|
</ACard>
|
||||||
|
</div>
|
||||||
|
</SimpleScrollbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.h-full {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-wrapper {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-file-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-btn-link) {
|
||||||
|
padding: 0;
|
||||||
|
height: auto;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-btn-link:hover) {
|
||||||
|
color: #1890ff;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-modal-body) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-modal-confirm-content) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user