|
|
@@ -0,0 +1,313 @@
|
|
|
+<script setup lang="ts">
|
|
|
+import { reactive, onMounted, ref, nextTick } from "vue";
|
|
|
+import { getWalletDetailList } from "@/api/finance";
|
|
|
+import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
|
|
+import { ElMessage } from "element-plus";
|
|
|
+
|
|
|
+defineOptions({
|
|
|
+ name: "AdminFinanceWalletFlow"
|
|
|
+});
|
|
|
+
|
|
|
+const queryRef = ref();
|
|
|
+const tableRef = ref();
|
|
|
+
|
|
|
+const typeOptions = [
|
|
|
+ { value: undefined, label: "全部" },
|
|
|
+ { value: 1, label: "充值" },
|
|
|
+ { value: 2, label: "提现" },
|
|
|
+ { value: 3, label: "消费" }
|
|
|
+];
|
|
|
+
|
|
|
+const statusOptions = [
|
|
|
+ { value: undefined, label: "全部" },
|
|
|
+ { value: 0, label: "待确认" },
|
|
|
+ { value: 1, label: "已确认" },
|
|
|
+ { value: 2, label: "已取消" }
|
|
|
+];
|
|
|
+
|
|
|
+const typeTagMap: Record<number, { text: string; type: string }> = {
|
|
|
+ 1: { text: "充值", type: "success" },
|
|
|
+ 2: { text: "提现", type: "warning" },
|
|
|
+ 3: { text: "消费", type: "danger" }
|
|
|
+};
|
|
|
+
|
|
|
+const statusTagMap: Record<number, { text: string; type: string }> = {
|
|
|
+ 0: { text: "待确认", type: "info" },
|
|
|
+ 1: { text: "已确认", type: "success" },
|
|
|
+ 2: { text: "已取消", type: "danger" }
|
|
|
+};
|
|
|
+
|
|
|
+const state = reactive({
|
|
|
+ formQuery: {
|
|
|
+ mobilePhone: "",
|
|
|
+ type: undefined as number | undefined,
|
|
|
+ status: undefined as number | undefined,
|
|
|
+ startDate: "",
|
|
|
+ endDate: ""
|
|
|
+ },
|
|
|
+ pageQuery: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ total: 0
|
|
|
+ },
|
|
|
+ tableData: {
|
|
|
+ height: 500,
|
|
|
+ data: [] as Array<any>,
|
|
|
+ loading: false,
|
|
|
+ columns: [
|
|
|
+ { label: "用户ID", prop: "userId", width: 120 },
|
|
|
+ { label: "用户手机号", prop: "mobilePhone", width: 140 },
|
|
|
+ { label: "交易类型", prop: "type", width: 100 },
|
|
|
+ { label: "订单号", prop: "orderNo", width: 200 },
|
|
|
+ { label: "金额", prop: "amount", width: 110 },
|
|
|
+ { label: "赠款金额", prop: "grantsAmount", width: 110 },
|
|
|
+ { label: "手续费", prop: "commission", width: 100 },
|
|
|
+ { label: "交易前余额", prop: "beforeBalance", width: 120 },
|
|
|
+ { label: "交易后余额", prop: "afterBalance", width: 120 },
|
|
|
+ { label: "交易前赠款", prop: "beforeGrantsBalance", width: 120 },
|
|
|
+ { label: "交易后赠款", prop: "afterGrantsBalance", width: 120 },
|
|
|
+ { label: "状态", prop: "status", width: 100 },
|
|
|
+ { label: "交易时间", prop: "transactionTime", width: 170 },
|
|
|
+ { label: "备注", prop: "remark", minWidth: 150 }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ loadData();
|
|
|
+ nextTick(() => {
|
|
|
+ const bodyHeight = document.body.clientHeight;
|
|
|
+ const queryHeight = queryRef.value?.$el?.clientHeight || 0;
|
|
|
+ state.tableData.height = bodyHeight - queryHeight - 280;
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+const buildParams = () => {
|
|
|
+ const params: any = { ...state.formQuery, ...state.pageQuery };
|
|
|
+ if (!params.type) delete params.type;
|
|
|
+ if (params.status === undefined || params.status === null) delete params.status;
|
|
|
+ if (!params.startDate) delete params.startDate;
|
|
|
+ if (!params.endDate) delete params.endDate;
|
|
|
+ return params;
|
|
|
+};
|
|
|
+
|
|
|
+const loadData = (refresh: boolean = false) => {
|
|
|
+ if (refresh) {
|
|
|
+ state.pageQuery.pageNum = 1;
|
|
|
+ }
|
|
|
+ state.tableData.loading = true;
|
|
|
+ getWalletDetailList(buildParams())
|
|
|
+ .then((res: any) => {
|
|
|
+ const { list, total } = res || {};
|
|
|
+ state.tableData.data = list || [];
|
|
|
+ state.pageQuery.total = total || 0;
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ state.tableData.data = [];
|
|
|
+ ElMessage.error("加载资金流水记录失败");
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ state.tableData.loading = false;
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const handleSizeChange = (size: number) => {
|
|
|
+ state.pageQuery.pageSize = size;
|
|
|
+ loadData(true);
|
|
|
+};
|
|
|
+
|
|
|
+const handleCurrentChange = (page: number) => {
|
|
|
+ state.pageQuery.pageNum = page;
|
|
|
+ loadData();
|
|
|
+};
|
|
|
+
|
|
|
+const handleSearch = () => {
|
|
|
+ loadData(true);
|
|
|
+};
|
|
|
+
|
|
|
+const handleReset = () => {
|
|
|
+ state.formQuery = {
|
|
|
+ mobilePhone: "",
|
|
|
+ type: undefined,
|
|
|
+ status: undefined,
|
|
|
+ startDate: "",
|
|
|
+ endDate: ""
|
|
|
+ };
|
|
|
+ loadData(true);
|
|
|
+};
|
|
|
+
|
|
|
+const formatMoney = (value: number) => {
|
|
|
+ if (value === null || value === undefined) return "-";
|
|
|
+ return `${(value / 100).toFixed(2)}`;
|
|
|
+};
|
|
|
+
|
|
|
+const getTypeTag = (type: number) => {
|
|
|
+ return typeTagMap[type] || { text: "未知", type: "info" };
|
|
|
+};
|
|
|
+
|
|
|
+const getStatusTag = (status: number) => {
|
|
|
+ return statusTagMap[status] || { text: "未知", type: "info" };
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <div class="page-container">
|
|
|
+ <el-card shadow="hover">
|
|
|
+ <template #header>
|
|
|
+ <span class="card-header">用户资金流</span>
|
|
|
+ </template>
|
|
|
+ <el-form
|
|
|
+ ref="queryRef"
|
|
|
+ :model="state.formQuery"
|
|
|
+ inline
|
|
|
+ class="search-form"
|
|
|
+ >
|
|
|
+ <el-form-item label="用户手机号">
|
|
|
+ <el-input
|
|
|
+ v-model="state.formQuery.mobilePhone"
|
|
|
+ placeholder="请输入手机号"
|
|
|
+ clearable
|
|
|
+ style="width: 160px"
|
|
|
+ @keyup.enter="handleSearch"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="交易类型">
|
|
|
+ <el-select
|
|
|
+ v-model="state.formQuery.type"
|
|
|
+ placeholder="全部"
|
|
|
+ style="width: 120px"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="opt in typeOptions"
|
|
|
+ :key="opt.value"
|
|
|
+ :label="opt.label"
|
|
|
+ :value="opt.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="状态">
|
|
|
+ <el-select
|
|
|
+ v-model="state.formQuery.status"
|
|
|
+ placeholder="全部"
|
|
|
+ style="width: 120px"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="opt in statusOptions"
|
|
|
+ :key="opt.value"
|
|
|
+ :label="opt.label"
|
|
|
+ :value="opt.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="交易时间">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="state.formQuery.startDate"
|
|
|
+ type="date"
|
|
|
+ placeholder="开始日期"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ style="width: 150px"
|
|
|
+ />
|
|
|
+ <span style="margin: 0 8px; color: #999">至</span>
|
|
|
+ <el-date-picker
|
|
|
+ v-model="state.formQuery.endDate"
|
|
|
+ type="date"
|
|
|
+ placeholder="结束日期"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ style="width: 150px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ :icon="useRenderIcon('ri/search-line')"
|
|
|
+ @click="handleSearch"
|
|
|
+ >
|
|
|
+ 查询
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ :icon="useRenderIcon('ri/refresh-line')"
|
|
|
+ @click="handleReset"
|
|
|
+ >
|
|
|
+ 重置
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-table
|
|
|
+ ref="tableRef"
|
|
|
+ v-loading="state.tableData.loading"
|
|
|
+ :data="state.tableData.data"
|
|
|
+ :height="state.tableData.height"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ >
|
|
|
+ <template #empty>
|
|
|
+ <el-empty description="暂无资金流水记录" />
|
|
|
+ </template>
|
|
|
+ <el-table-column
|
|
|
+ v-for="col in state.tableData.columns"
|
|
|
+ :key="col.prop"
|
|
|
+ :prop="col.prop"
|
|
|
+ :label="col.label"
|
|
|
+ :width="col.width"
|
|
|
+ :min-width="col.minWidth"
|
|
|
+ show-overflow-tooltip
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <template v-if="col.prop === 'type'">
|
|
|
+ <el-tag :type="getTypeTag(row.type).type" size="small" effect="plain">
|
|
|
+ {{ getTypeTag(row.type).text }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="col.prop === 'status'">
|
|
|
+ <el-tag :type="getStatusTag(row.status).type" size="small" effect="plain">
|
|
|
+ {{ getStatusTag(row.status).text }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="['amount', 'grantsAmount', 'commission', 'beforeBalance', 'afterBalance', 'beforeGrantsBalance', 'afterGrantsBalance'].includes(col.prop)">
|
|
|
+ {{ formatMoney(row[col.prop]) }}
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ {{ row[col.prop] }}
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <div class="pagination-container">
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="state.pageQuery.pageNum"
|
|
|
+ v-model:page-size="state.pageQuery.pageSize"
|
|
|
+ :total="state.pageQuery.total"
|
|
|
+ :page-sizes="[10, 20, 50, 100]"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ @current-change="handleCurrentChange"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.page-container {
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.card-header {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.search-form {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.pagination-container {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ margin-top: 20px;
|
|
|
+}
|
|
|
+</style>
|