feat(notice): 新增商户通知任务页与订阅退款事件

管理端列表/重发与多语种文案,应用通知配置补充 refund 订阅选项。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
DaxPay Dev
2026-07-21 13:47:37 +08:00
parent 76ee778632
commit 243b293fa0
34 changed files with 666 additions and 19 deletions

View File

@@ -0,0 +1,76 @@
import type { MchEntity, PageResult, Result } from '#/types/web';
import { defHttp } from '#/api/request';
/**
* 商户出站通知管理 API
*/
export const MchNoticeApi = {
/** 通知任务分页 */
pageTask(params: MchNoticeTaskQuery & { current?: number; size?: number }): Promise<Result<PageResult<MchNoticeTaskResult>>> {
return defHttp.get({ url: '/admin/mch-notice/task/page', params });
},
/** 通知任务详情 */
getTaskById(id: string): Promise<Result<MchNoticeTaskResult>> {
return defHttp.get({ url: '/admin/mch-notice/task/get-by-id', params: { id } });
},
/** 手动重发 */
resend(id: string): Promise<Result<void>> {
return defHttp.post({ url: '/admin/mch-notice/task/resend', params: { id } });
},
/** 发送记录分页 */
pageRecord(params: MchNoticeRecordQuery & { current?: number; size?: number }): Promise<Result<PageResult<MchNoticeRecordResult>>> {
return defHttp.get({ url: '/admin/mch-notice/record/page', params });
},
};
/** 通知任务查询 */
export interface MchNoticeTaskQuery {
mchNo?: string;
appId?: string;
bizNo?: string;
event?: string;
protocol?: string;
source?: string;
success?: boolean;
}
/** 通知任务结果 */
export interface MchNoticeTaskResult extends MchEntity {
appId?: string;
bizId?: string;
bizNo?: string;
event?: string;
protocol?: string;
source?: string;
contentMode?: string;
content?: string;
url?: string;
success?: boolean;
sendCount?: number;
delayCount?: number;
nextTime?: string;
latestTime?: string;
errorMsg?: string;
}
/** 发送记录查询 */
export interface MchNoticeRecordQuery {
taskId?: string;
mchNo?: string;
success?: boolean;
}
/** 发送记录结果 */
export interface MchNoticeRecordResult extends MchEntity {
taskId?: string;
reqCount?: number;
sendType?: string;
success?: boolean;
httpStatus?: number;
errorMsg?: string;
requestDigest?: string;
}

View File

@@ -185,6 +185,10 @@ export const PermCodes = {
VIEW: 'trade:gateway-order:view',
MANAGE: 'trade:gateway-order:manage',
},
Notice: {
VIEW: 'trade:mch-notice:view',
MANAGE: 'trade:mch-notice:manage',
},
},
Develop: {

View File

@@ -18,6 +18,7 @@
"remark": "Remark",
"remarkPlaceholder": "Please enter remark",
"confirmSave": "Save async notify config?",
"saveSuccess": "Saved successfully"
"saveSuccess": "Saved successfully",
"eventRefund": "Refund"
}
}

View File

@@ -0,0 +1,30 @@
{
"title": "Merchant Notify",
"bizNo": "Biz No.",
"event": "Event",
"protocol": "Protocol",
"source": "Source",
"url": "Notify URL",
"success": "Success",
"sendCount": "Send Count",
"delayCount": "Retry Count",
"latestTime": "Last Sent",
"nextTime": "Next Send",
"errorMsg": "Error",
"content": "Payload",
"resend": "Resend",
"confirmResend": "Resend this notification?",
"records": "Send Records",
"reqCount": "Seq",
"sendType": "Send Type",
"httpStatus": "HTTP Status",
"requestDigest": "Request Digest",
"yes": "Yes",
"no": "No",
"placeholderBizNo": "Enter biz no.",
"sourceOrder": "Order",
"sourceApp": "App",
"sourceProtocol": "Protocol",
"protocolSystem": "System",
"protocolEasyPay": "EasyPay"
}

View File

@@ -18,6 +18,7 @@
"remark": "Komentar",
"remarkPlaceholder": "Silakan masukkan komentar",
"confirmSave": "Simpan konfigurasi pemberitahuan async?",
"saveSuccess": "Berhasil disimpan"
"saveSuccess": "Berhasil disimpan",
"eventRefund": "Refund"
}
}

View File

@@ -0,0 +1,30 @@
{
"title": "Merchant Notify",
"bizNo": "Biz No.",
"event": "Event",
"protocol": "Protocol",
"source": "Source",
"url": "Notify URL",
"success": "Success",
"sendCount": "Send Count",
"delayCount": "Retry Count",
"latestTime": "Last Sent",
"nextTime": "Next Send",
"errorMsg": "Error",
"content": "Payload",
"resend": "Resend",
"confirmResend": "Resend this notification?",
"records": "Send Records",
"reqCount": "Seq",
"sendType": "Send Type",
"httpStatus": "HTTP Status",
"requestDigest": "Request Digest",
"yes": "Yes",
"no": "No",
"placeholderBizNo": "Enter biz no.",
"sourceOrder": "Order",
"sourceApp": "App",
"sourceProtocol": "Protocol",
"protocolSystem": "System",
"protocolEasyPay": "EasyPay"
}

View File

@@ -18,6 +18,7 @@
"remark": "述べる",
"remarkPlaceholder": "備考を入力してください",
"confirmSave": "非同期通知設定を保存しますか?",
"saveSuccess": "正常に保存されました"
"saveSuccess": "正常に保存されました",
"eventRefund": "返金"
}
}

View File

@@ -0,0 +1,30 @@
{
"title": "加盟店通知",
"bizNo": "業務番号",
"event": "イベント",
"protocol": "プロトコル",
"source": "ソース",
"url": "通知URL",
"success": "成功",
"sendCount": "送信回数",
"delayCount": "リトライ回数",
"latestTime": "最終送信",
"nextTime": "次回送信",
"errorMsg": "エラー",
"content": "内容",
"resend": "再送",
"confirmResend": "この通知を再送しますか?",
"records": "送信記録",
"reqCount": "番号",
"sendType": "送信種別",
"httpStatus": "HTTP状態",
"requestDigest": "リクエスト要約",
"yes": "はい",
"no": "いいえ",
"placeholderBizNo": "業務番号を入力",
"sourceOrder": "注文",
"sourceApp": "アプリ",
"sourceProtocol": "プロトコル",
"protocolSystem": "システム",
"protocolEasyPay": "易支付"
}

View File

@@ -18,6 +18,7 @@
"remark": "주목",
"remarkPlaceholder": "비고를 입력해주세요",
"confirmSave": "비동기 알림 구성을 저장하시겠습니까?",
"saveSuccess": "성공적으로 저장되었습니다"
"saveSuccess": "성공적으로 저장되었습니다",
"eventRefund": "환불"
}
}

View File

@@ -0,0 +1,30 @@
{
"title": "가맹점 알림",
"bizNo": "업무번호",
"event": "이벤트",
"protocol": "프로토콜",
"source": "출처",
"url": "알림 URL",
"success": "성공",
"sendCount": "전송 횟수",
"delayCount": "재시도",
"latestTime": "최근 전송",
"nextTime": "다음 전송",
"errorMsg": "오류",
"content": "내용",
"resend": "재전송",
"confirmResend": "이 알림을 다시 보내시겠습니까?",
"records": "전송 기록",
"reqCount": "순번",
"sendType": "전송 유형",
"httpStatus": "HTTP 상태",
"requestDigest": "요청 요약",
"yes": "예",
"no": "아니오",
"placeholderBizNo": "업무번호 입력",
"sourceOrder": "주문",
"sourceApp": "앱",
"sourceProtocol": "프로토콜",
"protocolSystem": "시스템",
"protocolEasyPay": "EasyPay"
}

View File

@@ -18,6 +18,7 @@
"remark": "Teguran",
"remarkPlaceholder": "Sila masukkan catatan",
"confirmSave": "Simpan konfigurasi pemberitahuan async?",
"saveSuccess": "Berjaya disimpan"
"saveSuccess": "Berjaya disimpan",
"eventRefund": "Bayaran balik"
}
}

View File

@@ -0,0 +1,30 @@
{
"title": "Merchant Notify",
"bizNo": "Biz No.",
"event": "Event",
"protocol": "Protocol",
"source": "Source",
"url": "Notify URL",
"success": "Success",
"sendCount": "Send Count",
"delayCount": "Retry Count",
"latestTime": "Last Sent",
"nextTime": "Next Send",
"errorMsg": "Error",
"content": "Payload",
"resend": "Resend",
"confirmResend": "Resend this notification?",
"records": "Send Records",
"reqCount": "Seq",
"sendType": "Send Type",
"httpStatus": "HTTP Status",
"requestDigest": "Request Digest",
"yes": "Yes",
"no": "No",
"placeholderBizNo": "Enter biz no.",
"sourceOrder": "Order",
"sourceApp": "App",
"sourceProtocol": "Protocol",
"protocolSystem": "System",
"protocolEasyPay": "EasyPay"
}

View File

@@ -18,6 +18,7 @@
"remark": "หมายเหตุ",
"remarkPlaceholder": "กรุณาใส่หมายเหตุ",
"confirmSave": "บันทึกการตั้งค่าการแจ้งเตือน async หรือไม่",
"saveSuccess": "บันทึกเรียบร้อยแล้ว"
"saveSuccess": "บันทึกเรียบร้อยแล้ว",
"eventRefund": "คืนเงิน"
}
}

View File

@@ -0,0 +1,30 @@
{
"title": "Merchant Notify",
"bizNo": "Biz No.",
"event": "Event",
"protocol": "Protocol",
"source": "Source",
"url": "Notify URL",
"success": "Success",
"sendCount": "Send Count",
"delayCount": "Retry Count",
"latestTime": "Last Sent",
"nextTime": "Next Send",
"errorMsg": "Error",
"content": "Payload",
"resend": "Resend",
"confirmResend": "Resend this notification?",
"records": "Send Records",
"reqCount": "Seq",
"sendType": "Send Type",
"httpStatus": "HTTP Status",
"requestDigest": "Request Digest",
"yes": "Yes",
"no": "No",
"placeholderBizNo": "Enter biz no.",
"sourceOrder": "Order",
"sourceApp": "App",
"sourceProtocol": "Protocol",
"protocolSystem": "System",
"protocolEasyPay": "EasyPay"
}

View File

@@ -18,6 +18,7 @@
"remark": "Nhận xét",
"remarkPlaceholder": "Vui lòng nhập nhận xét",
"confirmSave": "Lưu cấu hình thông báo không đồng bộ?",
"saveSuccess": "Đã lưu thành công"
"saveSuccess": "Đã lưu thành công",
"eventRefund": "Hoàn tiền"
}
}

View File

@@ -0,0 +1,30 @@
{
"title": "Merchant Notify",
"bizNo": "Biz No.",
"event": "Event",
"protocol": "Protocol",
"source": "Source",
"url": "Notify URL",
"success": "Success",
"sendCount": "Send Count",
"delayCount": "Retry Count",
"latestTime": "Last Sent",
"nextTime": "Next Send",
"errorMsg": "Error",
"content": "Payload",
"resend": "Resend",
"confirmResend": "Resend this notification?",
"records": "Send Records",
"reqCount": "Seq",
"sendType": "Send Type",
"httpStatus": "HTTP Status",
"requestDigest": "Request Digest",
"yes": "Yes",
"no": "No",
"placeholderBizNo": "Enter biz no.",
"sourceOrder": "Order",
"sourceApp": "App",
"sourceProtocol": "Protocol",
"protocolSystem": "System",
"protocolEasyPay": "EasyPay"
}

View File

@@ -18,6 +18,7 @@
"remark": "备注",
"remarkPlaceholder": "请输入备注",
"confirmSave": "确定要保存异步通知配置吗?",
"saveSuccess": "保存成功"
"saveSuccess": "保存成功",
"eventRefund": "退款"
}
}

View File

@@ -0,0 +1,30 @@
{
"title": "商户通知",
"bizNo": "业务单号",
"event": "事件",
"protocol": "协议",
"source": "来源",
"url": "回调地址",
"success": "发送成功",
"sendCount": "发送次数",
"delayCount": "重试次数",
"latestTime": "最后发送",
"nextTime": "下次发送",
"errorMsg": "错误信息",
"content": "通知内容",
"resend": "重发",
"confirmResend": "确认重新发送该通知?",
"records": "发送记录",
"reqCount": "序号",
"sendType": "发送类型",
"httpStatus": "HTTP状态",
"requestDigest": "请求摘要",
"yes": "是",
"no": "否",
"placeholderBizNo": "请输入业务单号",
"sourceOrder": "订单级",
"sourceApp": "应用级",
"sourceProtocol": "协议",
"protocolSystem": "系统",
"protocolEasyPay": "易支付"
}

View File

@@ -18,6 +18,7 @@
"remark": "備註",
"remarkPlaceholder": "請輸入備註",
"confirmSave": "確定要保存異步通知配置嗎?",
"saveSuccess": "保存成功"
"saveSuccess": "保存成功",
"eventRefund": "退款"
}
}

View File

@@ -0,0 +1,30 @@
{
"title": "商戶通知",
"bizNo": "业务单号",
"event": "事件",
"protocol": "协议",
"source": "来源",
"url": "回调地址",
"success": "发送成功",
"sendCount": "发送次数",
"delayCount": "重试次数",
"latestTime": "最后发送",
"nextTime": "下次发送",
"errorMsg": "错误信息",
"content": "通知内容",
"resend": "重發",
"confirmResend": "確認重新發送該通知?",
"records": "發送記錄",
"reqCount": "序号",
"sendType": "发送类型",
"httpStatus": "HTTP状态",
"requestDigest": "请求摘要",
"yes": "是",
"no": "否",
"placeholderBizNo": "請輸入業務單號",
"sourceOrder": "訂單級",
"sourceApp": "應用級",
"sourceProtocol": "协议",
"protocolSystem": "系統",
"protocolEasyPay": "易支付"
}

View File

@@ -18,6 +18,7 @@
"remark": "備註",
"remarkPlaceholder": "請輸入備註",
"confirmSave": "確定要儲存非同步通知配置嗎?",
"saveSuccess": "儲存成功"
"saveSuccess": "儲存成功",
"eventRefund": "退款"
}
}

View File

@@ -0,0 +1,30 @@
{
"title": "商戶通知",
"bizNo": "业务单号",
"event": "事件",
"protocol": "协议",
"source": "来源",
"url": "回调地址",
"success": "发送成功",
"sendCount": "发送次数",
"delayCount": "重试次数",
"latestTime": "最后发送",
"nextTime": "下次发送",
"errorMsg": "错误信息",
"content": "通知内容",
"resend": "重發",
"confirmResend": "確認重新發送該通知?",
"records": "發送記錄",
"reqCount": "序号",
"sendType": "发送类型",
"httpStatus": "HTTP状态",
"requestDigest": "请求摘要",
"yes": "是",
"no": "否",
"placeholderBizNo": "請輸入業務單號",
"sourceOrder": "訂單級",
"sourceApp": "應用級",
"sourceProtocol": "协议",
"protocolSystem": "系統",
"protocolEasyPay": "易支付"
}

View File

@@ -46,6 +46,7 @@
"menu.payment.config": "Payment Config",
"menu.payment.merchant.store": "Store Management",
"menu.trade.refundOrder": "Refund Orders",
"menu.trade.mchNotice": "Merchant Notify",
"menu.develop": "Develop",
"menu.device": "Device Management",
"menu.demos.artemis": "Message Queue Demo",

View File

@@ -94,5 +94,6 @@
"menu.system.security.system": "Keamanan Sistem",
"menu.system.security.pay": "Keamanan Pembayaran",
"menu.payment.merchant.terminal": "Terminal Sistem",
"menu.device.terminal.channel": "Terminal Saluran"
"menu.device.terminal.channel": "Terminal Saluran",
"menu.trade.mchNotice": "Notifikasi Merchant"
}

View File

@@ -94,5 +94,6 @@
"menu.system.security.system": "システムセキュリティ",
"menu.system.security.pay": "決済セキュリティ",
"menu.payment.merchant.terminal": "システム端末",
"menu.device.terminal.channel": "チャネル端末"
"menu.device.terminal.channel": "チャネル端末",
"menu.trade.mchNotice": "加盟店通知"
}

View File

@@ -94,5 +94,6 @@
"menu.system.security.system": "시스템 보안",
"menu.system.security.pay": "결제 보안",
"menu.payment.merchant.terminal": "시스템 단말",
"menu.device.terminal.channel": "채널 단말"
"menu.device.terminal.channel": "채널 단말",
"menu.trade.mchNotice": "가맹점 알림"
}

View File

@@ -94,5 +94,6 @@
"menu.system.security.system": "Keselamatan Sistem",
"menu.system.security.pay": "Keselamatan Pembayaran",
"menu.payment.merchant.terminal": "Terminal Sistem",
"menu.device.terminal.channel": "Terminal Saluran"
"menu.device.terminal.channel": "Terminal Saluran",
"menu.trade.mchNotice": "Notis Pedagang"
}

View File

@@ -94,5 +94,6 @@
"menu.system.security.system": "ความปลอดภัยระบบ",
"menu.system.security.pay": "ความปลอดภัยการชำระเงิน",
"menu.payment.merchant.terminal": "เทอร์มินัลระบบ",
"menu.device.terminal.channel": "เทอร์มินัลช่องทาง"
"menu.device.terminal.channel": "เทอร์มินัลช่องทาง",
"menu.trade.mchNotice": "การแจ้งเตือนร้านค้า"
}

View File

@@ -94,5 +94,6 @@
"menu.system.security.system": "Bảo mật hệ thống",
"menu.system.security.pay": "Bảo mật thanh toán",
"menu.payment.merchant.terminal": "Thiết bị hệ thống",
"menu.device.terminal.channel": "Thiết bị kênh"
"menu.device.terminal.channel": "Thiết bị kênh",
"menu.trade.mchNotice": "Thông báo merchant"
}

View File

@@ -46,6 +46,7 @@
"menu.payment.config": "支付配置",
"menu.payment.merchant.store": "门店管理",
"menu.trade.refundOrder": "退款订单",
"menu.trade.mchNotice": "商户通知",
"menu.develop": "开发调试",
"menu.device": "设备管理",
"menu.demos.artemis": "消息队列演示",

View File

@@ -94,5 +94,6 @@
"menu.system.security.system": "系統安全配置",
"menu.system.security.pay": "支付安全配置",
"menu.payment.merchant.terminal": "系統終端",
"menu.device.terminal.channel": "通道終端"
"menu.device.terminal.channel": "通道終端",
"menu.trade.mchNotice": "商戶通知"
}

View File

@@ -94,5 +94,6 @@
"menu.system.security.system": "系統安全配置",
"menu.system.security.pay": "支付安全配置",
"menu.payment.merchant.terminal": "系統終端",
"menu.device.terminal.channel": "通道終端"
"menu.device.terminal.channel": "通道終端",
"menu.trade.mchNotice": "商戶通知"
}

View File

@@ -44,10 +44,11 @@
});
/**
* 订阅事件选项(对应 TradeTypeEnum: pay/cashouts/settle)
* 订阅事件选项(前缀匹配: pay→pay.*, refund→refund.*)
*/
const eventOptions = computed(() => [
{ label: $t('payment.merchant.notifyConfig.notifyConfig.eventPay'), value: 'pay' },
{ label: $t('payment.merchant.notifyConfig.notifyConfig.eventRefund'), value: 'refund' },
{ label: $t('payment.merchant.notifyConfig.notifyConfig.eventCashouts'), value: 'cashouts' },
{ label: $t('payment.merchant.notifyConfig.notifyConfig.eventSettle'), value: 'settle' },
]);

View File

@@ -0,0 +1,246 @@
<script lang="ts" setup>
import type { VxeTableInstance, VxeToolbarInstance } from 'vxe-table';
import { computed, onMounted, ref } from 'vue';
import { $t } from '@vben/locales';
import {
MchNoticeApi,
type MchNoticeRecordResult,
type MchNoticeTaskQuery,
type MchNoticeTaskResult,
} from '#/api/payment/notice/mch-notice.api';
import { BQuery, type QueryField } from '#/components/query';
import { PermCodes } from '#/constants/perm-codes';
import { useMessage } from '#/hooks/useMessage';
import { usePermission } from '#/hooks/usePermission';
defineOptions({ name: 'MchNoticeTaskList' });
const { confirm, message } = useMessage();
const { hasPermission } = usePermission();
const loading = ref(false);
const xTable = ref<VxeTableInstance>();
const xToolbar = ref<VxeToolbarInstance>();
const queryForm = ref<MchNoticeTaskQuery>({});
const pageConfig = ref({ currentPage: 1, pageSize: 10, total: 0 });
const tableData = ref<MchNoticeTaskResult[]>([]);
const drawerVisible = ref(false);
const detail = ref<MchNoticeTaskResult>({});
const recordLoading = ref(false);
const records = ref<MchNoticeRecordResult[]>([]);
const actionLoading = ref(false);
const queryFields = computed<QueryField[]>(() => [
{
type: 'string',
field: 'bizNo',
// 业务单号
name: $t('payment.notice.mchNotice.bizNo'),
placeholder: $t('payment.notice.mchNotice.placeholderBizNo'),
},
{
type: 'string',
field: 'event',
// 事件
name: $t('payment.notice.mchNotice.event'),
},
{
type: 'string',
field: 'protocol',
// 协议
name: $t('payment.notice.mchNotice.protocol'),
},
{
type: 'string',
field: 'mchNo',
name: $t('payment.order.field.merchant'),
},
]);
function sourceLabel(source?: string) {
if (source === 'order') return $t('payment.notice.mchNotice.sourceOrder');
if (source === 'app') return $t('payment.notice.mchNotice.sourceApp');
if (source === 'protocol') return $t('payment.notice.mchNotice.sourceProtocol');
return source || '-';
}
function protocolLabel(protocol?: string) {
if (protocol === 'system') return $t('payment.notice.mchNotice.protocolSystem');
if (protocol === 'easy_pay') return $t('payment.notice.mchNotice.protocolEasyPay');
return protocol || '-';
}
async function queryPage() {
loading.value = true;
try {
const { data } = await MchNoticeApi.pageTask({
...queryForm.value,
current: pageConfig.value.currentPage,
size: pageConfig.value.pageSize,
});
tableData.value = data?.records || [];
pageConfig.value.total = Number(data?.total || 0);
} finally {
loading.value = false;
}
}
function resetQuery() {
queryForm.value = {};
pageConfig.value.currentPage = 1;
queryPage();
}
async function handleView(row: MchNoticeTaskResult) {
drawerVisible.value = true;
detail.value = row;
recordLoading.value = true;
try {
const { data } = await MchNoticeApi.pageRecord({
taskId: row.id,
current: 1,
size: 50,
});
records.value = data?.records || [];
} finally {
recordLoading.value = false;
}
}
function handleResend(row: MchNoticeTaskResult) {
confirm({
// 确认重发
content: $t('payment.notice.mchNotice.confirmResend'),
onOk() {
actionLoading.value = true;
return MchNoticeApi.resend(row.id!)
.then(() => {
message.success($t('common.success'));
queryPage();
})
.finally(() => {
actionLoading.value = false;
});
},
});
}
onMounted(() => {
xTable.value?.connectToolbar(xToolbar.value as VxeToolbarInstance);
queryPage();
});
</script>
<template>
<div class="m-3 p-3 bg-background rounded-lg list-page-compact">
<a-card>
<BQuery :fields="queryFields" :query-params="queryForm" @query="queryPage" @reset="resetQuery" />
</a-card>
<div class="mt-4">
<a-card>
<vxe-toolbar ref="xToolbar" custom refresh :refresh-options="{ queryMethod: queryPage }" />
<vxe-table ref="xTable" :row-config="{ keyField: 'id' }" :data="tableData" :loading="loading">
<vxe-column type="seq" :title="$t('common.seq')" width="60" align="center" />
<vxe-column field="mchName" :title="$t('payment.order.field.merchant')" :min-width="140">
<template #default="{ row }">
<div class="flex flex-col">
<span>{{ row.mchName || row.mchNo || '-' }}</span>
<span v-if="row.mchNo" class="text-xs text-muted-foreground">{{ row.mchNo }}</span>
</div>
</template>
</vxe-column>
<vxe-column field="bizNo" :title="$t('payment.notice.mchNotice.bizNo')" :min-width="180" show-overflow />
<vxe-column field="event" :title="$t('payment.notice.mchNotice.event')" :min-width="120" />
<vxe-column field="protocol" :title="$t('payment.notice.mchNotice.protocol')" :min-width="100">
<template #default="{ row }">{{ protocolLabel(row.protocol) }}</template>
</vxe-column>
<vxe-column field="source" :title="$t('payment.notice.mchNotice.source')" :min-width="90">
<template #default="{ row }">{{ sourceLabel(row.source) }}</template>
</vxe-column>
<vxe-column field="success" :title="$t('payment.notice.mchNotice.success')" width="100" align="center">
<template #default="{ row }">
<a-tag :color="row.success ? 'success' : 'default'">
{{ row.success ? $t('payment.notice.mchNotice.yes') : $t('payment.notice.mchNotice.no') }}
</a-tag>
</template>
</vxe-column>
<vxe-column field="sendCount" :title="$t('payment.notice.mchNotice.sendCount')" width="90" align="center" />
<vxe-column
field="latestTime"
:title="$t('payment.notice.mchNotice.latestTime')"
:min-width="160"
formatter="formatDateTime"
/>
<vxe-column fixed="right" :width="160" :show-overflow="false" :title="$t('common.operation')">
<template #default="{ row }">
<a-space :size="2">
<template #separator>
<a-divider type="vertical" />
</template>
<a-button type="link" size="small" @click="handleView(row)">
{{ $t('common.view') }}
</a-button>
<a-button
v-if="hasPermission(PermCodes.Trade.Notice.MANAGE) && !row.success"
type="link"
size="small"
:loading="actionLoading"
@click="handleResend(row)"
>
{{ $t('payment.notice.mchNotice.resend') }}
</a-button>
</a-space>
</template>
</vxe-column>
</vxe-table>
<vxe-pager
size="medium"
:loading="loading"
:current-page="pageConfig.currentPage"
:page-size="pageConfig.pageSize"
:total="pageConfig.total"
@page-change="
({ currentPage, pageSize }) => {
pageConfig.currentPage = currentPage;
pageConfig.pageSize = pageSize;
queryPage();
}
"
/>
</a-card>
</div>
<a-drawer
v-model:open="drawerVisible"
:title="$t('payment.notice.mchNotice.records')"
width="720"
destroy-on-close
>
<div class="mb-4 space-y-2 text-sm">
<div>{{ $t('payment.notice.mchNotice.bizNo') }}: {{ detail.bizNo }}</div>
<div>{{ $t('payment.notice.mchNotice.event') }}: {{ detail.event }}</div>
<div>{{ $t('payment.notice.mchNotice.url') }}: {{ detail.url }}</div>
<div v-if="detail.errorMsg">{{ $t('payment.notice.mchNotice.errorMsg') }}: {{ detail.errorMsg }}</div>
</div>
<vxe-table :data="records" :loading="recordLoading" :row-config="{ keyField: 'id' }">
<vxe-column field="reqCount" :title="$t('payment.notice.mchNotice.reqCount')" width="70" align="center" />
<vxe-column field="sendType" :title="$t('payment.notice.mchNotice.sendType')" width="90" />
<vxe-column field="success" :title="$t('payment.notice.mchNotice.success')" width="90" align="center">
<template #default="{ row }">
<a-tag :color="row.success ? 'success' : 'error'">
{{ row.success ? $t('payment.notice.mchNotice.yes') : $t('payment.notice.mchNotice.no') }}
</a-tag>
</template>
</vxe-column>
<vxe-column field="httpStatus" :title="$t('payment.notice.mchNotice.httpStatus')" width="100" />
<vxe-column field="errorMsg" :title="$t('payment.notice.mchNotice.errorMsg')" min-width="160" show-overflow />
<vxe-column field="createTime" :title="$t('payment.order.field.createTime')" min-width="160" formatter="formatDateTime" />
</vxe-table>
</a-drawer>
</div>
</template>