mirror of
https://gitee.com/bootx/dax-pay-ui
synced 2026-08-13 07:45:41 +08:00
feat(payment): 新增退款订单列表,订单页能力增强
- 新增 RefundOrderList 页与 refund-order API - NormalOrderList 增强,补全订单 i18n 文案 - 微调偏好/离线页配置
This commit is contained in:
110
apps/daxpay-admin/src/api/payment/order/refund-order.api.ts
Normal file
110
apps/daxpay-admin/src/api/payment/order/refund-order.api.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import type { MchEntity, PageResult, Result } from '#/types/web';
|
||||
|
||||
import { defHttp } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 退款订单管理 API
|
||||
*/
|
||||
export const RefundOrderApi = {
|
||||
/**
|
||||
* 分页查询退款订单
|
||||
*/
|
||||
page(params: RefundOrderQuery & { current?: number; size?: number }): Promise<Result<PageResult<RefundOrderResult>>> {
|
||||
return defHttp.get({ url: '/admin/order/refund/page', params });
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据ID查询退款订单详情
|
||||
*/
|
||||
getById(id: string): Promise<Result<RefundOrderResult>> {
|
||||
return defHttp.get({ url: '/admin/order/refund/get-by-id', params: { id } });
|
||||
},
|
||||
|
||||
/**
|
||||
* 发起退款
|
||||
*/
|
||||
refund(data: PayRefundParam): Promise<Result<RefundOrderResult>> {
|
||||
return defHttp.post({ url: '/admin/order/refund/refund', data });
|
||||
},
|
||||
|
||||
/**
|
||||
* 同步退款状态(主动向通道查询)
|
||||
*/
|
||||
sync(id: string): Promise<Result<RefundOrderResult>> {
|
||||
return defHttp.post({ url: '/admin/order/refund/sync', params: { id } });
|
||||
},
|
||||
};
|
||||
|
||||
/** 退款订单查询参数 */
|
||||
export interface RefundOrderQuery {
|
||||
/** 商户号 */
|
||||
mchNo?: string;
|
||||
/** 应用号 */
|
||||
appId?: string;
|
||||
/** 退款号 */
|
||||
refundNo?: string;
|
||||
/** 商户退款号 */
|
||||
bizRefundNo?: string;
|
||||
/** 原支付订单号 */
|
||||
orderNo?: string;
|
||||
/** 商户业务订单号 */
|
||||
bizOrderNo?: string;
|
||||
/** 退款状态 */
|
||||
status?: string;
|
||||
/** 支付通道 */
|
||||
channel?: string;
|
||||
/** 创建时间-开始 */
|
||||
createTimeStart?: string;
|
||||
/** 创建时间-结束 */
|
||||
createTimeEnd?: string;
|
||||
}
|
||||
|
||||
/** 退款发起参数 */
|
||||
export interface PayRefundParam {
|
||||
/** 原支付订单号 */
|
||||
orderNo?: string;
|
||||
/** 商户业务订单号 */
|
||||
bizOrderNo?: string;
|
||||
/** 退款金额(分) */
|
||||
amount: number;
|
||||
/** 退款原因 */
|
||||
reason?: string;
|
||||
/** 商户退款号(可选) */
|
||||
bizRefundNo?: string;
|
||||
}
|
||||
|
||||
/** 退款订单结果 */
|
||||
export interface RefundOrderResult extends MchEntity {
|
||||
/** 退款号 */
|
||||
refundNo?: string;
|
||||
/** 商户退款号 */
|
||||
bizRefundNo?: string;
|
||||
/** 标题 */
|
||||
title?: string;
|
||||
/** 原支付订单号 */
|
||||
orderNo?: string;
|
||||
/** 商户业务订单号 */
|
||||
bizOrderNo?: string;
|
||||
/** 通道支付订单号 */
|
||||
outOrderNo?: string;
|
||||
/** 通道退款流水号 */
|
||||
outRefundNo?: string;
|
||||
/** 退款金额(分) */
|
||||
amount?: number;
|
||||
/** 订单总金额(分) */
|
||||
orderAmount?: number;
|
||||
/** 退款原因 */
|
||||
reason?: string;
|
||||
/** 退款状态 */
|
||||
status?: string;
|
||||
/** 退款完成时间 */
|
||||
finishTime?: string;
|
||||
/** 支付通道 */
|
||||
channel?: string;
|
||||
/** 支付产品 */
|
||||
product?: string;
|
||||
/** 支付方式 */
|
||||
method?: string;
|
||||
/** 错误信息 */
|
||||
errorMsg?: string;
|
||||
}
|
||||
@@ -144,6 +144,11 @@ export const PermCodes = {
|
||||
VIEW: 'payment:order:view',
|
||||
MANAGE: 'payment:order:manage',
|
||||
},
|
||||
/** 退款订单 menuCode=payment:refund */
|
||||
Refund: {
|
||||
VIEW: 'payment:refund:view',
|
||||
MANAGE: 'payment:refund:manage',
|
||||
},
|
||||
/** 资金交易凭证 menuCode=payment:trade */
|
||||
Trade: {
|
||||
VIEW: 'payment:trade:view',
|
||||
|
||||
@@ -7,6 +7,24 @@
|
||||
"title": "Fund Trades",
|
||||
"detail": "Fund Trade Detail"
|
||||
},
|
||||
"refund": {
|
||||
"title": "Refund Orders",
|
||||
"detail": "Refund Order Detail",
|
||||
"status": {
|
||||
"init": "Initial",
|
||||
"progress": "Refunding",
|
||||
"success": "Success",
|
||||
"fail": "Failed",
|
||||
"close": "Closed"
|
||||
},
|
||||
"statusColor": {
|
||||
"init": "default",
|
||||
"progress": "processing",
|
||||
"success": "green",
|
||||
"fail": "red",
|
||||
"close": "default"
|
||||
}
|
||||
},
|
||||
"field": {
|
||||
"bizOrderNo": "Merchant Order No.",
|
||||
"tradeNo": "Trade No.",
|
||||
@@ -52,7 +70,14 @@
|
||||
"limitPay": "Limit Pay",
|
||||
"mchNo": "Merchant No.",
|
||||
"appId": "App ID",
|
||||
"createTime": "Create Time"
|
||||
"createTime": "Create Time",
|
||||
"refundNo": "Refund No.",
|
||||
"bizRefundNo": "Biz Refund No.",
|
||||
"outRefundNo": "Channel Refund No.",
|
||||
"reason": "Refund Reason",
|
||||
"finishTime": "Finish Time",
|
||||
"orderAmount": "Order Amount",
|
||||
"orderNo": "Original Order No."
|
||||
},
|
||||
"bizStatus": {
|
||||
"wait_pay": "Waiting",
|
||||
@@ -86,16 +111,28 @@
|
||||
"tradeNo": "Enter trade no.",
|
||||
"outOrderNo": "Enter channel order no.",
|
||||
"amountMin": "Min amount(cents)",
|
||||
"amountMax": "Max amount(cents)"
|
||||
"amountMax": "Max amount(cents)",
|
||||
"refundNo": "Enter refund no.",
|
||||
"bizRefundNo": "Enter biz refund no.",
|
||||
"orderNo": "Enter original order no."
|
||||
},
|
||||
"action": {
|
||||
"sync": "Sync Status",
|
||||
"close": "Close Order",
|
||||
"refund": "Refund",
|
||||
"syncSuccess": "Sync succeeded",
|
||||
"closeSuccess": "Order closed",
|
||||
"refundSuccess": "Refund initiated",
|
||||
"syncConfirmTitle": "Confirm Sync",
|
||||
"syncConfirmContent": "Will query the latest status from the payment channel, continue?",
|
||||
"closeConfirmTitle": "Confirm Close",
|
||||
"closeConfirmContent": "The order cannot be paid after closing, continue?"
|
||||
"closeConfirmContent": "The order cannot be paid after closing, continue?",
|
||||
"refundConfirmTitle": "Confirm Refund",
|
||||
"refundConfirmContent": "Will refund {amount} from the original order, continue?",
|
||||
"refundAmountLabel": "Refund Amount(Yuan)",
|
||||
"refundReasonLabel": "Refund Reason",
|
||||
"refundAmountPlaceholder": "Enter refund amount",
|
||||
"refundReasonPlaceholder": "Enter refund reason (optional)",
|
||||
"refundAmountExceed": "Refund amount cannot exceed refundable amount"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,24 @@
|
||||
"title": "资金交易",
|
||||
"detail": "资金交易详情"
|
||||
},
|
||||
"refund": {
|
||||
"title": "退款订单",
|
||||
"detail": "退款订单详情",
|
||||
"status": {
|
||||
"init": "初始化",
|
||||
"progress": "退款中",
|
||||
"success": "退款成功",
|
||||
"fail": "退款失败",
|
||||
"close": "退款关闭"
|
||||
},
|
||||
"statusColor": {
|
||||
"init": "default",
|
||||
"progress": "processing",
|
||||
"success": "green",
|
||||
"fail": "red",
|
||||
"close": "default"
|
||||
}
|
||||
},
|
||||
"field": {
|
||||
"bizOrderNo": "商户订单号",
|
||||
"tradeNo": "资金交易号",
|
||||
@@ -52,7 +70,14 @@
|
||||
"limitPay": "限制支付类型",
|
||||
"mchNo": "商户号",
|
||||
"appId": "应用号",
|
||||
"createTime": "创建时间"
|
||||
"createTime": "创建时间",
|
||||
"refundNo": "退款号",
|
||||
"bizRefundNo": "商户退款号",
|
||||
"outRefundNo": "通道退款流水号",
|
||||
"reason": "退款原因",
|
||||
"finishTime": "退款完成时间",
|
||||
"orderAmount": "订单总金额",
|
||||
"orderNo": "原支付订单号"
|
||||
},
|
||||
"bizStatus": {
|
||||
"wait_pay": "待支付",
|
||||
@@ -86,16 +111,28 @@
|
||||
"tradeNo": "请输入资金交易号",
|
||||
"outOrderNo": "请输入通道订单号",
|
||||
"amountMin": "最小金额(分)",
|
||||
"amountMax": "最大金额(分)"
|
||||
"amountMax": "最大金额(分)",
|
||||
"refundNo": "请输入退款号",
|
||||
"bizRefundNo": "请输入商户退款号",
|
||||
"orderNo": "请输入原支付订单号"
|
||||
},
|
||||
"action": {
|
||||
"sync": "同步状态",
|
||||
"close": "关闭订单",
|
||||
"refund": "退款",
|
||||
"syncSuccess": "同步成功",
|
||||
"closeSuccess": "关闭成功",
|
||||
"refundSuccess": "退款发起成功",
|
||||
"syncConfirmTitle": "确认同步状态",
|
||||
"syncConfirmContent": "将主动向支付通道查询该订单的最新状态,是否继续?",
|
||||
"closeConfirmTitle": "确认关闭订单",
|
||||
"closeConfirmContent": "关闭后订单将不可继续支付,是否继续?"
|
||||
"closeConfirmContent": "关闭后订单将不可继续支付,是否继续?",
|
||||
"refundConfirmTitle": "确认发起退款",
|
||||
"refundConfirmContent": "退款将从原支付订单扣除 {amount} 元,是否继续?",
|
||||
"refundAmountLabel": "退款金额(元)",
|
||||
"refundReasonLabel": "退款原因",
|
||||
"refundAmountPlaceholder": "请输入退款金额",
|
||||
"refundReasonPlaceholder": "请输入退款原因(可选)",
|
||||
"refundAmountExceed": "退款金额不能大于可退金额"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ export const overridesPreferences = defineOverridesPreferences({
|
||||
app: {
|
||||
accessMode: 'backend',
|
||||
name: 'DaxPay Admin',
|
||||
// 默认首页路径,覆盖框架默认的 /analytics,登录后直接进入工作台
|
||||
defaultHomePath: '/workspace',
|
||||
},
|
||||
logo: {
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
<script lang="ts" setup>
|
||||
import { Fallback } from '@vben/common-ui';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
defineOptions({ name: 'FallbackOfflineDemo' });
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 重试连接: 跳回首页重新走认证流程
|
||||
// (后端已恢复则正常进入系统, 仍不可用则由守卫重新跳回本页, 不会死循环)
|
||||
function retry() {
|
||||
router.replace(preferences.app.defaultHomePath);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Fallback status="offline" />
|
||||
<Fallback status="offline">
|
||||
<template #action>
|
||||
<a-button type="primary" size="large" @click="retry">
|
||||
{{ $t('common.refresh') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</Fallback>
|
||||
</template>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
|
||||
import { IconifyIcon } from '@vben-core/icons';
|
||||
|
||||
import { AlipayMchAppApi, type AlipayMchApp } from '#/api/payment/channel/alipay/mch-app.api';
|
||||
import { type AlipayMchApp, AlipayMchAppApi } from '#/api/payment/channel/alipay/mch-app.api';
|
||||
import RouteQueryMissingState from '#/components/route/RouteQueryMissingState.vue';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { ProductEnum } from '#/enums/payment/productEnum';
|
||||
import { normalizeRouteQueryValue, useRequiredRouteQuery } from '#/hooks/useRequiredRouteQuery';
|
||||
import { usePermission } from '#/hooks/usePermission';
|
||||
import { normalizeRouteQueryValue, useRequiredRouteQuery } from '#/hooks/useRequiredRouteQuery';
|
||||
|
||||
import AlipayMchAppCard from './AlipayMchAppCard.vue';
|
||||
import AlipayMchAppDetail from './AlipayMchAppDetail.vue';
|
||||
@@ -39,9 +39,7 @@
|
||||
if (mchNo && id) {
|
||||
return { path: '/payment/merchant/channel-merchant/detail', query: { mchNo, id, product } };
|
||||
}
|
||||
return mchNo
|
||||
? { path: '/payment/merchant/channel-merchant', query: { mchNo } }
|
||||
: '/payment/merchant';
|
||||
return mchNo ? { path: '/payment/merchant/channel-merchant', query: { mchNo } } : '/payment/merchant';
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { NormalOrderApi, type NormalOrderQuery, type NormalOrderResult } from '#/api/payment/order/normal-order.api';
|
||||
import { RefundOrderApi, type PayRefundParam } from '#/api/payment/order/refund-order.api';
|
||||
import { BQuery, type QueryField } from '#/components/query';
|
||||
import { PermCodes } from '#/constants/perm-codes';
|
||||
import { useMessage } from '#/hooks/useMessage';
|
||||
@@ -37,6 +38,12 @@
|
||||
const detail = ref<NormalOrderResult>({});
|
||||
const actionLoading = ref(false);
|
||||
|
||||
// 退款弹窗
|
||||
const refundVisible = ref(false);
|
||||
const refundLoading = ref(false);
|
||||
const refundForm = ref<PayRefundParam & { orderNo?: string }>({ amount: 0, reason: '' });
|
||||
const refundRow = ref<NormalOrderResult | null>(null);
|
||||
|
||||
// 业务状态下拉
|
||||
const statusOptions = computed(() =>
|
||||
['wait_pay', 'paid', 'closed', 'expired'].map((v) => ({
|
||||
@@ -231,6 +238,56 @@
|
||||
detail.value = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开退款弹窗
|
||||
*/
|
||||
function handleRefund(row: NormalOrderResult) {
|
||||
refundRow.value = row;
|
||||
// 默认退款金额为可退金额(分转元)
|
||||
const refundable = row.refundableBalance ?? detail.value.refundableBalance ?? 0;
|
||||
refundForm.value = {
|
||||
orderNo: row.tradeNo || detail.value.tradeNo,
|
||||
amount: refundable,
|
||||
reason: '',
|
||||
};
|
||||
refundVisible.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交退款
|
||||
*/
|
||||
function submitRefund() {
|
||||
if (!refundRow.value) return;
|
||||
// 校验退款金额不能超过可退金额
|
||||
const refundable = (refundRow.value.refundableBalance ?? 0);
|
||||
if (refundForm.value.amount > refundable) {
|
||||
message.error($t('payment.order.action.refundAmountExceed'));
|
||||
return;
|
||||
}
|
||||
const yuanAmount = (refundForm.value.amount / 100).toFixed(2);
|
||||
confirm({
|
||||
title: $t('payment.order.action.refundConfirmTitle'),
|
||||
content: $t('payment.order.action.refundConfirmContent', { amount: yuanAmount }),
|
||||
onOk() {
|
||||
refundLoading.value = true;
|
||||
return RefundOrderApi.refund(refundForm.value)
|
||||
.then(() => {
|
||||
message.success($t('payment.order.action.refundSuccess'));
|
||||
refundVisible.value = false;
|
||||
queryPage();
|
||||
})
|
||||
.finally(() => {
|
||||
refundLoading.value = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleRefundClose() {
|
||||
refundVisible.value = false;
|
||||
refundRow.value = null;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
xTable.value?.connectToolbar(xToolbar.value as VxeToolbarInstance);
|
||||
queryPage();
|
||||
@@ -309,6 +366,15 @@
|
||||
>
|
||||
{{ $t('payment.order.action.close') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="hasPermission(PermCodes.Payment.Refund.MANAGE) && row.status === 'paid'"
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
@click="handleRefund(row)"
|
||||
>
|
||||
{{ $t('payment.order.action.refund') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-column>
|
||||
@@ -420,5 +486,36 @@
|
||||
<a-button @click="handleDrawerClose">{{ $t('common.close') }}</a-button>
|
||||
</template>
|
||||
</a-drawer>
|
||||
|
||||
<!-- 退款弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="refundVisible"
|
||||
:title="$t('payment.order.action.refund')"
|
||||
:confirm-loading="refundLoading"
|
||||
@ok="submitRefund"
|
||||
@cancel="handleRefundClose"
|
||||
>
|
||||
<a-form :label-col="{ span: 6 }">
|
||||
<a-form-item :label="$t('payment.order.action.refundAmountLabel')">
|
||||
<a-input-number
|
||||
v-model:value="refundForm.amount"
|
||||
:min="1"
|
||||
:max="refundRow?.refundableBalance"
|
||||
style="width: 100%"
|
||||
:placeholder="$t('payment.order.action.refundAmountPlaceholder')"
|
||||
/>
|
||||
<div v-if="refundRow" style="font-size: 12px; color: #999; margin-top: 4px">
|
||||
{{ $t('payment.order.field.refundableBalance') }}: {{ formatAmount(refundRow.refundableBalance) }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('payment.order.action.refundReasonLabel')">
|
||||
<a-textarea
|
||||
v-model:value="refundForm.reason"
|
||||
:rows="2"
|
||||
:placeholder="$t('payment.order.action.refundReasonPlaceholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
329
apps/daxpay-admin/src/views/payment/order/RefundOrderList.vue
Normal file
329
apps/daxpay-admin/src/views/payment/order/RefundOrderList.vue
Normal file
@@ -0,0 +1,329 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableInstance, VxeToolbarInstance } from 'vxe-table';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { RefundOrderApi, type RefundOrderQuery, type RefundOrderResult } from '#/api/payment/order/refund-order.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: 'RefundOrderList' });
|
||||
|
||||
const { confirm, message } = useMessage();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const loading = ref(false);
|
||||
const xTable = ref<VxeTableInstance>();
|
||||
const xToolbar = ref<VxeToolbarInstance>();
|
||||
|
||||
// 查询条件
|
||||
const queryForm = ref<RefundOrderQuery>({});
|
||||
|
||||
const pageConfig = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const tableData = ref<RefundOrderResult[]>([]);
|
||||
|
||||
// 详情抽屉
|
||||
const drawerVisible = ref(false);
|
||||
const drawerLoading = ref(false);
|
||||
const detail = ref<RefundOrderResult>({});
|
||||
const actionLoading = ref(false);
|
||||
|
||||
// 退款状态下拉
|
||||
const statusOptions = computed(() =>
|
||||
['init', 'progress', 'success', 'fail', 'close'].map((v) => ({
|
||||
label: $t(`payment.order.refund.status.${v}`),
|
||||
value: v,
|
||||
})),
|
||||
);
|
||||
|
||||
// 支付通道下拉(常用通道)
|
||||
const channelOptions = [
|
||||
{ label: '支付宝', value: 'alipay' },
|
||||
{ label: '微信支付', value: 'wechat' },
|
||||
{ label: '抖音支付', value: 'douyin' },
|
||||
];
|
||||
|
||||
// 查询字段
|
||||
const queryFields = computed<QueryField[]>(() => [
|
||||
{
|
||||
type: 'string',
|
||||
field: 'refundNo',
|
||||
name: $t('payment.order.field.refundNo'),
|
||||
placeholder: $t('payment.order.placeholder.refundNo'),
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
field: 'orderNo',
|
||||
name: $t('payment.order.field.orderNo'),
|
||||
placeholder: $t('payment.order.placeholder.orderNo'),
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
field: 'bizOrderNo',
|
||||
name: $t('payment.order.field.bizOrderNo'),
|
||||
placeholder: $t('payment.order.placeholder.bizOrderNo'),
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
field: 'status',
|
||||
name: $t('payment.order.field.bizStatus'),
|
||||
selectList: statusOptions.value,
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
field: 'channel',
|
||||
name: $t('payment.order.field.channel'),
|
||||
selectList: channelOptions,
|
||||
},
|
||||
{
|
||||
type: 'date_time_range',
|
||||
field: 'createTime',
|
||||
name: $t('payment.order.field.createTime'),
|
||||
startField: 'createTimeStart',
|
||||
endField: 'createTimeEnd',
|
||||
},
|
||||
]);
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*/
|
||||
function queryPage() {
|
||||
loading.value = true;
|
||||
return RefundOrderApi.page({
|
||||
current: pageConfig.value.currentPage,
|
||||
size: pageConfig.value.pageSize,
|
||||
...queryForm.value,
|
||||
})
|
||||
.then((res) => {
|
||||
tableData.value = res.data?.records || [];
|
||||
pageConfig.value.total = Number(res.data?.total) || 0;
|
||||
loading.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function resetQuery() {
|
||||
queryForm.value = {};
|
||||
pageConfig.value.currentPage = 1;
|
||||
queryPage();
|
||||
}
|
||||
|
||||
function handlePageChange({ currentPage, pageSize }: { currentPage: number; pageSize: number }) {
|
||||
pageConfig.value.currentPage = currentPage;
|
||||
pageConfig.value.pageSize = pageSize;
|
||||
queryPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额分转元
|
||||
*/
|
||||
function formatAmount(amount?: number): string {
|
||||
if (amount === null || amount === undefined) return '-';
|
||||
return (amount / 100).toFixed(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款状态颜色
|
||||
*/
|
||||
function statusColor(status?: string): string {
|
||||
return status ? $t(`payment.order.refund.statusColor.${status}`) : 'default';
|
||||
}
|
||||
|
||||
function channelLabel(code?: string): string {
|
||||
if (!code) return '-';
|
||||
return channelOptions.find((o) => o.value === code)?.label || code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看详情
|
||||
*/
|
||||
async function handleView(row: RefundOrderResult) {
|
||||
drawerVisible.value = true;
|
||||
drawerLoading.value = true;
|
||||
try {
|
||||
const { data } = await RefundOrderApi.getById(row.id!);
|
||||
detail.value = data || {};
|
||||
} finally {
|
||||
drawerLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步退款状态
|
||||
*/
|
||||
function handleSync(row: RefundOrderResult) {
|
||||
confirm({
|
||||
title: $t('payment.order.action.syncConfirmTitle'),
|
||||
content: $t('payment.order.action.syncConfirmContent'),
|
||||
onOk() {
|
||||
actionLoading.value = true;
|
||||
return RefundOrderApi.sync(row.id!)
|
||||
.then(() => {
|
||||
message.success($t('payment.order.action.syncSuccess'));
|
||||
queryPage();
|
||||
})
|
||||
.finally(() => {
|
||||
actionLoading.value = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleDrawerClose() {
|
||||
drawerVisible.value = false;
|
||||
detail.value = {};
|
||||
}
|
||||
|
||||
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="refundNo" :title="$t('payment.order.field.refundNo')" :min-width="200" show-overflow />
|
||||
<vxe-column field="orderNo" :title="$t('payment.order.field.orderNo')" :min-width="200" show-overflow />
|
||||
<vxe-column field="bizOrderNo" :title="$t('payment.order.field.bizOrderNo')" :min-width="180" show-overflow />
|
||||
<vxe-column field="amount" :title="$t('payment.order.field.amount')" :min-width="100" align="right">
|
||||
<template #default="{ row }">
|
||||
{{ formatAmount(row.amount) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="status" :title="$t('payment.order.field.bizStatus')" :min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<a-tag :color="statusColor(row.status)">
|
||||
{{ $t(`payment.order.refund.status.${row.status}`) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="channel" :title="$t('payment.order.field.channel')" :min-width="100">
|
||||
<template #default="{ row }">{{ channelLabel(row.channel) }}</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="finishTime"
|
||||
:title="$t('payment.order.field.finishTime')"
|
||||
:min-width="160"
|
||||
formatter="formatDateTime"
|
||||
/>
|
||||
<vxe-column
|
||||
field="createTime"
|
||||
:title="$t('payment.order.field.createTime')"
|
||||
:min-width="160"
|
||||
formatter="formatDateTime"
|
||||
/>
|
||||
<vxe-column :title="$t('common.operation')" width="160" fixed="right" :show-overflow="false">
|
||||
<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.Payment.Refund.MANAGE)"
|
||||
type="link"
|
||||
size="small"
|
||||
:loading="actionLoading"
|
||||
@click="handleSync(row)"
|
||||
>
|
||||
{{ $t('payment.order.action.sync') }}
|
||||
</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="handlePageChange"
|
||||
/>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<!-- 详情抽屉 -->
|
||||
<a-drawer
|
||||
v-model:open="drawerVisible"
|
||||
:title="$t('payment.order.refund.detail')"
|
||||
:size="700"
|
||||
@close="handleDrawerClose"
|
||||
>
|
||||
<a-spin :spinning="drawerLoading">
|
||||
<a-descriptions :column="2" size="small" bordered>
|
||||
<a-descriptions-item :label="$t('payment.order.field.refundNo')">
|
||||
{{ detail.refundNo || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.bizRefundNo')">
|
||||
{{ detail.bizRefundNo || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.orderNo')">
|
||||
{{ detail.orderNo || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.bizOrderNo')">
|
||||
{{ detail.bizOrderNo || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.outRefundNo')">
|
||||
{{ detail.outRefundNo || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.bizStatus')">
|
||||
<a-tag :color="statusColor(detail.status)">
|
||||
{{ detail.status ? $t(`payment.order.refund.status.${detail.status}`) : '-' }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.amount')">
|
||||
{{ formatAmount(detail.amount) }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.orderAmount')">
|
||||
{{ formatAmount(detail.orderAmount) }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.channel')">
|
||||
{{ channelLabel(detail.channel) }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.method')">
|
||||
{{ detail.method || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.reason')" :span="2">
|
||||
{{ detail.reason || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.finishTime')">
|
||||
{{ detail.finishTime || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.createTime')">
|
||||
{{ detail.createTime || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="$t('payment.order.field.errorMsg')" :span="2">
|
||||
{{ detail.errorMsg || '-' }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-spin>
|
||||
|
||||
<template #footer>
|
||||
<a-button @click="handleDrawerClose">{{ $t('common.close') }}</a-button>
|
||||
</template>
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
@@ -17,7 +17,7 @@ const defaultPreferences: Preferences = {
|
||||
contentPaddingTop: 0,
|
||||
defaultAvatar:
|
||||
'https://unpkg.com/@vbenjs/static-source@0.1.7/source/avatar-v1.webp',
|
||||
defaultHomePath: '/analytics',
|
||||
defaultHomePath: '/workspace',
|
||||
dynamicTitle: true,
|
||||
enableCheckUpdates: true,
|
||||
enableCopyPreferences: true,
|
||||
|
||||
Reference in New Issue
Block a user