|
@@ -0,0 +1,295 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <el-card>
|
|
|
|
|
+ <template #header>
|
|
|
|
|
+ <div class="card-header">
|
|
|
|
|
+ <span>批量付款批次</span>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-hasPerm="['module_payment:account:transfer:list']"
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ icon="Download"
|
|
|
|
|
+ :loading="exportLoading"
|
|
|
|
|
+ @click="handleExport"
|
|
|
|
|
+ >
|
|
|
|
|
+ 导出报表
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <div class="mb-4">
|
|
|
|
|
+ <el-form :inline="true" :model="searchForm">
|
|
|
|
|
+ <el-form-item v-if="isPlatformUser" label="企业">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="searchForm.enterprise_id"
|
|
|
|
|
+ placeholder="选择企业"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ filterable
|
|
|
|
|
+ style="width: 180px"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="e in enterpriseStore.getEnterpriseList"
|
|
|
|
|
+ :key="e.enterprise_id"
|
|
|
|
|
+ :label="e.name"
|
|
|
|
|
+ :value="e.enterprise_id"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="状态">
|
|
|
|
|
+ <el-select v-model="searchForm.status" placeholder="全部" clearable style="width: 140px">
|
|
|
|
|
+ <el-option v-for="(t, s) in STATUS_TEXT" :key="s" :label="t" :value="s" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="时间范围">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="searchForm.dateRange"
|
|
|
|
|
+ type="daterange"
|
|
|
|
|
+ range-separator="至"
|
|
|
|
|
+ start-placeholder="开始日期"
|
|
|
|
|
+ end-placeholder="结束日期"
|
|
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
|
|
+ style="width: 260px"
|
|
|
|
|
+ @change="handleDateChange"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item>
|
|
|
|
|
+ <el-button type="primary" @click="handleSearch">查询</el-button>
|
|
|
|
|
+ <el-button @click="handleSearchReset">重置</el-button>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table v-loading="loading" :data="list" border stripe>
|
|
|
|
|
+ <template #empty>
|
|
|
|
|
+ <el-empty description="暂无数据" />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <el-table-column prop="out_batch_no" label="批次号" min-width="200" />
|
|
|
|
|
+ <el-table-column prop="order_title" label="标题" min-width="140">
|
|
|
|
|
+ <template #default="{ row }">{{ row.order_title || "-" }}</template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column prop="total_amount" label="总金额(元)" width="110">
|
|
|
|
|
+ <template #default="{ row }">¥{{ row.total_amount }}</template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column prop="total_count" label="笔数" width="70" />
|
|
|
|
|
+ <el-table-column label="状态" width="90">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ <el-tag :type="STATUS_TAG[row.status] || 'info'">
|
|
|
|
|
+ {{ STATUS_TEXT[row.status] || row.status }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column prop="created_time" label="创建时间" width="170">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ {{ row.created_time ? dayjs(row.created_time).format("YYYY-MM-DD HH:mm:ss") : "-" }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="操作" width="210" fixed="right">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-if="row.status === 'INIT'"
|
|
|
|
|
+ v-hasPerm="['module_payment:account:transfer']"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ :loading="payingNo === row.out_batch_no"
|
|
|
|
|
+ @click="handlePay(row)"
|
|
|
|
|
+ >
|
|
|
|
|
+ 支付
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-hasPerm="['module_payment:account:transfer:detail']"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @click="emit('view', row.out_batch_no, row.enterprise_id)"
|
|
|
|
|
+ >
|
|
|
|
|
+ 详情
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-if="row.status === 'INIT'"
|
|
|
|
|
+ v-hasPerm="['module_payment:account:transfer']"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ type="danger"
|
|
|
|
|
+ @click="handleClose(row)"
|
|
|
|
|
+ >
|
|
|
|
|
+ 关闭
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ <div 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, reactive, ref } from "vue";
|
|
|
|
|
+import BatchPayAPI, { type BatchOrderVO } from "@/api/module_payment/batch";
|
|
|
|
|
+import { useEnterpriseStore, useUserStore } from "@/store";
|
|
|
|
|
+import { ElMessage, ElMessageBox } from "element-plus";
|
|
|
|
|
+import dayjs from "dayjs";
|
|
|
|
|
+
|
|
|
|
|
+const emit = defineEmits<{ view: [outBatchNo: string, enterpriseId?: string] }>();
|
|
|
|
|
+
|
|
|
|
|
+const enterpriseStore = useEnterpriseStore();
|
|
|
|
|
+const userStore = useUserStore();
|
|
|
|
|
+const isPlatformUser = computed(() => userStore.is_platform_user);
|
|
|
|
|
+const currentEnterpriseId = computed(() => enterpriseStore.getCurrentEnterprise?.enterprise_id);
|
|
|
|
|
+
|
|
|
|
|
+const list = ref<BatchOrderVO[]>([]);
|
|
|
|
|
+const total = ref(0);
|
|
|
|
|
+const pageNo = ref(1);
|
|
|
|
|
+const pageSize = ref(20);
|
|
|
|
|
+const loading = ref(false);
|
|
|
|
|
+const exportLoading = ref(false);
|
|
|
|
|
+const payingNo = ref("");
|
|
|
|
|
+
|
|
|
|
|
+const searchForm = reactive({
|
|
|
|
|
+ enterprise_id: undefined as string | undefined,
|
|
|
|
|
+ status: "",
|
|
|
|
|
+ dateRange: null as string[] | null,
|
|
|
|
|
+ start_time: undefined as string | undefined,
|
|
|
|
|
+ end_time: undefined as string | undefined,
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+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: "失败",
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+async function load() {
|
|
|
|
|
+ loading.value = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await BatchPayAPI.batchList({
|
|
|
|
|
+ enterprise_id: searchForm.enterprise_id || undefined,
|
|
|
|
|
+ status: searchForm.status || undefined,
|
|
|
|
|
+ start_time: searchForm.start_time || undefined,
|
|
|
|
|
+ end_time: searchForm.end_time || undefined,
|
|
|
|
|
+ page_no: pageNo.value,
|
|
|
|
|
+ page_size: pageSize.value,
|
|
|
|
|
+ });
|
|
|
|
|
+ list.value = res.data.data?.items || [];
|
|
|
|
|
+ total.value = res.data.data.total || 0;
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ loading.value = false;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handleSearch() {
|
|
|
|
|
+ pageNo.value = 1;
|
|
|
|
|
+ load();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handleDateChange() {
|
|
|
|
|
+ if (searchForm.dateRange && searchForm.dateRange.length === 2) {
|
|
|
|
|
+ searchForm.start_time = searchForm.dateRange[0];
|
|
|
|
|
+ searchForm.end_time = searchForm.dateRange[1];
|
|
|
|
|
+ } else {
|
|
|
|
|
+ searchForm.start_time = undefined;
|
|
|
|
|
+ searchForm.end_time = undefined;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handleSearchReset() {
|
|
|
|
|
+ searchForm.enterprise_id = undefined;
|
|
|
|
|
+ searchForm.status = "";
|
|
|
|
|
+ searchForm.dateRange = null;
|
|
|
|
|
+ searchForm.start_time = undefined;
|
|
|
|
|
+ searchForm.end_time = undefined;
|
|
|
|
|
+ handleSearch();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function handlePay(row: BatchOrderVO) {
|
|
|
|
|
+ const enterpriseId = row.enterprise_id || currentEnterpriseId.value;
|
|
|
|
|
+ if (!enterpriseId) {
|
|
|
|
|
+ ElMessage.warning("未获取到企业信息,无法生成支付页面");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ payingNo.value = row.out_batch_no;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await BatchPayAPI.renderPay(enterpriseId, row.out_batch_no);
|
|
|
|
|
+ // pageExecute 返回 HTML 表单,自带自动提交(与充值/签约链接处理一致)
|
|
|
|
|
+ const payHtml = res.data.data?.pay_url || "";
|
|
|
|
|
+ if (!payHtml) {
|
|
|
|
|
+ ElMessage.warning("未获取到支付页面");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ document.open();
|
|
|
|
|
+ document.write(payHtml);
|
|
|
|
|
+ document.close();
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ payingNo.value = "";
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function handleClose(row: BatchOrderVO) {
|
|
|
|
|
+ const enterpriseId = row.enterprise_id || currentEnterpriseId.value;
|
|
|
|
|
+ if (!enterpriseId) {
|
|
|
|
|
+ ElMessage.warning("未获取到企业信息,无法关闭批次");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ await ElMessageBox.confirm("关闭后该批次不可再支付,确定关闭?", "提示", { type: "warning" });
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ await BatchPayAPI.batchClose(enterpriseId, row.out_batch_no);
|
|
|
|
|
+ ElMessage.success("批次已关闭");
|
|
|
|
|
+ load();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function handleExport() {
|
|
|
|
|
+ exportLoading.value = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await BatchPayAPI.batchExport({
|
|
|
|
|
+ enterprise_id: searchForm.enterprise_id || undefined,
|
|
|
|
|
+ status: searchForm.status || undefined,
|
|
|
|
|
+ start_time: searchForm.start_time || undefined,
|
|
|
|
|
+ end_time: searchForm.end_time || undefined,
|
|
|
|
|
+ });
|
|
|
|
|
+ const blob = new Blob([res.data], {
|
|
|
|
|
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
|
|
|
+ });
|
|
|
|
|
+ const url = window.URL.createObjectURL(blob);
|
|
|
|
|
+ const a = document.createElement("a");
|
|
|
|
|
+ a.href = url;
|
|
|
|
|
+ a.download = "批量付款报表.xlsx";
|
|
|
|
|
+ document.body.appendChild(a);
|
|
|
|
|
+ a.click();
|
|
|
|
|
+ document.body.removeChild(a);
|
|
|
|
|
+ window.URL.revokeObjectURL(url);
|
|
|
|
|
+ ElMessage.success("导出成功");
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ ElMessage.error("导出失败,请稍后重试");
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ exportLoading.value = false;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ if (!isPlatformUser.value) {
|
|
|
|
|
+ searchForm.enterprise_id = currentEnterpriseId.value;
|
|
|
|
|
+ }
|
|
|
|
|
+ load();
|
|
|
|
|
+});
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
|
+.card-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|