| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- <template>
- <el-card v-loading="loading">
- <template v-if="order" #header>
- <div class="card-header">
- <span>批次详情 {{ order.out_batch_no }}</span>
- <div>
- <el-button
- v-if="order.status === 'INIT'"
- v-hasPerm="['module_payment:account:transfer']"
- type="primary"
- :loading="payLoading"
- @click="handlePay"
- >
- 生成支付链接
- </el-button>
- <el-button
- v-if="order.status === 'INIT'"
- v-hasPerm="['module_payment:account:transfer']"
- type="danger"
- @click="handleClose"
- >
- 关闭批次
- </el-button>
- <el-button icon="Refresh" :loading="refreshing" @click="refresh">刷新</el-button>
- </div>
- </div>
- </template>
- <el-descriptions v-if="order" :column="3" border style="margin-bottom: 16px">
- <el-descriptions-item label="标题">{{ order.order_title || "-" }}</el-descriptions-item>
- <el-descriptions-item label="总金额(元)">¥{{ order.total_amount }}</el-descriptions-item>
- <el-descriptions-item label="笔数">{{ order.total_count }}</el-descriptions-item>
- <el-descriptions-item label="状态">
- <el-tag :type="STATUS_TAG[order.status] || 'info'">
- {{ STATUS_TEXT[order.status] || order.status }}
- </el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="支付宝批次号">
- {{ order.batch_trans_id || "-" }}
- </el-descriptions-item>
- <el-descriptions-item label="创建时间">
- {{ order.created_time ? dayjs(order.created_time).format("YYYY-MM-DD HH:mm:ss") : "-" }}
- </el-descriptions-item>
- <el-descriptions-item label="转账场景">
- {{ order.transfer_scene_name || "-" }}
- </el-descriptions-item>
- <el-descriptions-item label="付款方UID">{{ order.payer_uid || "-" }}</el-descriptions-item>
- <el-descriptions-item label="备注">{{ order.remark || "-" }}</el-descriptions-item>
- <el-descriptions-item v-if="order.error_msg" label="错误信息">
- <span style="color: #f56c6c">{{ order.error_msg }}</span>
- </el-descriptions-item>
- </el-descriptions>
- <el-table v-if="order" :data="details" border stripe>
- <template #empty>
- <el-empty description="暂无数据" />
- </template>
- <el-table-column prop="out_biz_no" label="明细单号" min-width="200" />
- <el-table-column prop="payee_name" label="收款方姓名" width="120">
- <template #default="{ row }">{{ row.payee_name || "-" }}</template>
- </el-table-column>
- <el-table-column prop="payee_identity" label="收款方账号" width="200" />
- <el-table-column prop="amount" label="金额(元)" width="110">
- <template #default="{ row }">¥{{ row.amount }}</template>
- </el-table-column>
- <el-table-column label="状态" width="90">
- <template #default="{ row }">
- <el-tag :type="DETAIL_STATUS_TAG[row.status] || 'info'">
- {{ DETAIL_STATUS_TEXT[row.status] || row.status }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="error_msg" label="失败原因" min-width="140">
- <template #default="{ row }">{{ row.error_msg || "-" }}</template>
- </el-table-column>
- </el-table>
- <div v-if="order" class="mt-4 flex justify-end">
- <el-pagination
- v-model:current-page="pageNo"
- v-model:page-size="pageSize"
- :total="total"
- :page-sizes="[10, 20, 50, 100]"
- layout="total, sizes, prev, pager, next, jumper"
- @size-change="load"
- @current-change="load"
- />
- </div>
- </el-card>
- </template>
- <script setup lang="ts">
- import { computed, onMounted, ref } from "vue";
- import BatchPayAPI, { type BatchDetailItem, type BatchOrderVO } from "@/api/module_payment/batch";
- import { useEnterpriseStore } from "@/store";
- import { ElMessage, ElMessageBox } from "element-plus";
- import dayjs from "dayjs";
- const props = defineProps<{ outBatchNo: string; enterpriseId?: string }>();
- const enterpriseStore = useEnterpriseStore();
- const currentEnterpriseId = computed(() => enterpriseStore.getCurrentEnterprise?.enterprise_id);
- const enterpriseId = computed(() => props.enterpriseId || currentEnterpriseId.value);
- const order = ref<BatchOrderVO | null>(null);
- const details = ref<BatchDetailItem[]>([]);
- const total = ref(0);
- const pageNo = ref(1);
- const pageSize = ref(20);
- const loading = ref(false);
- const payLoading = ref(false);
- const refreshing = ref(false);
- type TagType = "primary" | "success" | "warning" | "info" | "danger";
- const STATUS_TAG: Record<string, TagType> = {
- INIT: "warning",
- SUCCESS: "success",
- DISUSE: "info",
- FAIL: "danger",
- };
- const STATUS_TEXT: Record<string, string> = {
- INIT: "受理中",
- SUCCESS: "成功",
- DISUSE: "已关闭",
- FAIL: "失败",
- };
- const DETAIL_STATUS_TAG: Record<string, TagType> = {
- INIT: "info",
- SUCCESS: "success",
- FAIL: "danger",
- };
- const DETAIL_STATUS_TEXT: Record<string, string> = {
- INIT: "处理中",
- SUCCESS: "成功",
- FAIL: "失败",
- };
- async function load() {
- if (!enterpriseId.value) {
- ElMessage.warning("未获取到企业信息");
- return;
- }
- loading.value = true;
- try {
- const res = await BatchPayAPI.batchDetail(
- enterpriseId.value,
- props.outBatchNo,
- pageNo.value,
- pageSize.value
- );
- order.value = res.data.data?.order || null;
- // 双保险解包: 后端 PageResult 序列化字段是 list(与存量 index.vue 一致)
- details.value = res.data.data?.details?.items || res.data.data?.details?.list || [];
- total.value = res.data.data?.details?.total || 0;
- } finally {
- loading.value = false;
- }
- }
- /** 刷新: 先调 batchQuery 同步支付宝侧批次+明细状态,再拉取本地详情展示(I1 手动兜底) */
- async function refresh() {
- if (!enterpriseId.value) {
- ElMessage.warning("未获取到企业信息");
- return;
- }
- refreshing.value = true;
- try {
- await BatchPayAPI.batchQuery(enterpriseId.value, props.outBatchNo);
- } catch {
- // 同步失败不阻塞本地展示(batchQuery 失败原因已由 request.ts 提示)
- } finally {
- refreshing.value = false;
- }
- await load();
- }
- async function handlePay() {
- if (!order.value || !enterpriseId.value) return;
- payLoading.value = true;
- try {
- const res = await BatchPayAPI.renderPay(enterpriseId.value, order.value.out_batch_no);
- // pageExecute 返回 HTML 表单,自带自动提交(与充值/签约链接处理一致)
- const payHtml = res.data.data?.pay_url || "";
- if (!payHtml) {
- ElMessage.warning("未获取到支付页面");
- return;
- }
- // 新窗口打开支付页(设计 M4): 避免 document.write 同页替换销毁当前 SPA
- const w = window.open("about:blank", "_blank");
- if (w) {
- w.document.write(payHtml);
- w.document.close();
- } else {
- ElMessage.warning("支付页弹窗被浏览器拦截,请允许弹窗后重试");
- }
- } finally {
- payLoading.value = false;
- }
- }
- async function handleClose() {
- if (!order.value || !enterpriseId.value) return;
- try {
- await ElMessageBox.confirm("关闭后该批次不可再支付,确定关闭?", "提示", { type: "warning" });
- } catch {
- return;
- }
- await BatchPayAPI.batchClose(enterpriseId.value, order.value.out_batch_no);
- ElMessage.success("批次已关闭");
- load();
- }
- onMounted(load);
- </script>
- <style lang="scss" scoped>
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- </style>
|