| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- <template>
- <div class="app-container h-full flex flex-1 flex-col">
- <!-- 搜索 -->
- <page-search
- ref="searchRef"
- :search-config="searchConfig"
- @query-click="handleQueryClick"
- @reset-click="handleResetClick"
- ></page-search>
- <!-- 列表 -->
- <page-content
- ref="contentRef"
- :content-config="contentConfig"
- @add-click="handleAddClick"
- @export-click="handleExportClick"
- @search-click="handleSearchClick"
- @toolbar-click="handleToolbarClick"
- @operate-click="handleOperateClick"
- @filter-change="handleFilterChange"
- @sort-change="handleSortChange"
- >
- <template #balance="scope">
- <span class="money-text primary">¥ {{ scope.row.balance }}</span>
- </template>
- <template #totalConsumption="scope">
- <span class="money-text warning">¥ {{ formatMoney(scope.row.totalConsumption) }}</span>
- </template>
- </page-content>
- <!-- 详情 -->
- <page-modal
- ref="editModalRef"
- :modal-config="editModalConfig"
- @submit-click="handleSubmitClick"
- ></page-modal>
- <!-- 退款记录弹窗 -->
- <el-dialog
- v-model="refundRecordDialogVisible"
- title="退款记录"
- width="80%"
- :close-on-click-modal="false"
- >
- <div class="refund-record-container">
- <!-- 搜索区域 -->
- <el-form :inline="true" :model="refundQueryParams" class="search-form">
- <el-form-item label="创建时间">
- <el-date-picker
- v-model="refundDateRange"
- type="daterange"
- range-separator="至"
- start-placeholder="开始日期"
- end-placeholder="结束日期"
- value-format="YYYY-MM-DD HH:mm:ss"
- :default-time="[new Date(0, 0, 0, 0, 0, 0), new Date(0, 0, 0, 23, 59, 59)]"
- @change="handleRefundDateChange"
- />
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="handleRefundQuery">查询</el-button>
- <el-button @click="handleRefundReset">重置</el-button>
- </el-form-item>
- </el-form>
- <!-- 表格 -->
- <el-table
- v-loading="refundTableLoading"
- :data="refundTableData"
- border
- style="width: 100%"
- >
- <el-table-column prop="orderId" label="订单ID" width="100" />
- <el-table-column prop="orderNo" label="商户订单号" width="180" />
- <el-table-column prop="type" label="退款类型" width="120">
- <template #default="{ row }">
- <el-tag :type="row.type === 1 ? 'warning' : 'info'">
- {{ row.type === 1 ? "主动退款" : "用户申请退款" }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="outRefundNo" label="商户退款单号" width="180" />
- <el-table-column prop="refundId" label="微信支付退款单号" width="200" />
- <el-table-column prop="transactionId" label="微信支付订单号" width="200" />
- <el-table-column prop="reason" label="退款原因" width="150" show-overflow-tooltip />
- <el-table-column prop="acceptedTime" label="退款受理时间" width="180" />
- <el-table-column prop="successTime" label="退款成功时间" width="180" />
- <el-table-column prop="amount" label="退款金额(元)" width="130">
- <template #default="{ row }">
- <span class="money-text danger">¥ {{ formatMoney(row.amount) }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="status" label="退款状态" width="120" />
- <el-table-column prop="createTime" label="创建时间" width="180" />
- <el-table-column label="操作" width="100">
- <template #default="{ row }">
- <el-button type="info" link @click="handleRefundRecordClick(row)">退款日志</el-button>
- </template>
- </el-table-column>
- </el-table>
- <!-- 分页 -->
- <el-pagination
- v-model:current-page="refundQueryParams.pageNum"
- v-model:page-size="refundQueryParams.pageSize"
- :total="refundTotal"
- :page-sizes="[10, 20, 30, 50]"
- layout="total, sizes, prev, pager, next, jumper"
- background
- @size-change="handleRefundSizeChange"
- @current-change="handleRefundCurrentChange"
- />
- </div>
- </el-dialog>
- <!-- 导出日期区间弹窗 -->
- <el-dialog
- v-model="exportDateDialogVisible"
- title="退款导出"
- width="420px"
- :close-on-click-modal="false"
- :align-center="true"
- >
- <el-form label-width="90px" style="padding: 8px 16px 0">
- <el-form-item label="区间选择:">
- <el-date-picker
- v-model="exportDateRange"
- type="daterange"
- range-separator="至"
- start-placeholder="开始日期"
- end-placeholder="结束日期"
- value-format="YYYY-MM-DD"
- style="width: 100%"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="exportDateDialogVisible = false">取 消</el-button>
- <el-button type="primary" :loading="exportBtnLoading" @click="handleExportConfirm">
- 确 定
- </el-button>
- </template>
- </el-dialog>
- <!-- 退款日志抽屉 -->
- <el-dialog
- v-model="refundLogDrawerVisible"
- title="退款日志"
- :size="200"
- :close-on-click-modal="false"
- >
- <el-form label-width="120px">
- <el-form-item label="通知请求">
- <el-input
- v-model="currentNotifyRequest"
- type="textarea"
- :rows="20"
- readonly
- placeholder="暂无数据"
- />
- </el-form-item>
- </el-form>
- </el-dialog>
- </div>
- </template>
- <script setup lang="ts">
- import UserOrderInfoAPI from "@/api/orderManage/user-order-info-api";
- defineOptions({ name: "UserInfo" });
- import UserInfoAPI, {
- UserInfoForm,
- UserInfoPageQuery,
- UserRefundOrderPageQuery,
- UserRefundOrderPageVO,
- } from "@/api/userManage/user-info-api";
- import UserFirmAPI from "@/api/toBManage/user-firm-api";
- import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
- import usePage from "@/components/CURD/usePage";
- // 组合式 CRUD
- const {
- searchRef,
- contentRef,
- editModalRef,
- handleQueryClick,
- handleResetClick,
- handleAddClick,
- handleEditClick,
- handleSubmitClick,
- handleSearchClick,
- handleFilterChange,
- } = usePage();
- // 金额格式化函数
- const formatMoney = (value: number | undefined | null): string => {
- if (value === null || value === undefined) return "0.00";
- return Math.abs(value).toLocaleString("zh-CN", {
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
- });
- };
- // 搜索配置
- const searchConfig: ISearchConfig = reactive({
- permPrefix: "business:user-info",
- formItems: [
- {
- type: "input",
- label: "手机号",
- prop: "phone",
- attrs: {
- placeholder: "手机号",
- clearable: true,
- style: { width: "200px" },
- },
- },
- {
- type: "select",
- label: "所属企业",
- prop: "firmId",
- attrs: {
- placeholder: "请选择企业",
- clearable: true,
- filterable: true,
- style: { width: "200px" },
- },
- options: [],
- async initFn(formItem) {
- try {
- const response = await UserFirmAPI.getFirmList();
- if (Array.isArray(response)) {
- formItem.options = response.map((item: any) => ({
- label: item.name,
- value: item.id,
- }));
- }
- } catch (error) {
- console.error("Failed to fetch firm list:", error);
- }
- },
- },
- ],
- });
- // 列表配置
- const contentConfig: IContentConfig<UserInfoPageQuery> = reactive({
- // 权限前缀
- permPrefix: "business:user-info",
- table: {
- border: true,
- highlightCurrentRow: true,
- },
- // 主键
- pk: "id",
- // 列表查询接口
- indexAction: UserInfoAPI.getPage,
- //退款导出
- exportAction: UserInfoAPI.exportExcel,
- // 数据解析函数
- parseData(res: any) {
- return {
- total: res.total,
- list: res.list,
- };
- },
- // 分页配置
- pagination: {
- background: true,
- layout: "total, sizes, prev, pager, next, jumper",
- pageSize: 20,
- pageSizes: [10, 20, 30, 50],
- },
- // 工具栏配置
- toolbar: ["export"],
- defaultToolbar: ["refresh", "filter"],
- // 表格列配置
- cols: [
- { type: "selection", width: 50, align: "center" },
- { label: "用户ID", prop: "id", width: 100 },
- { label: "手机号", prop: "phone" },
- { label: "昵称", prop: "nickName" },
- {
- label: "当前余额",
- prop: "balance",
- sortable: "custom",
- templet: "custom",
- slotName: "balance",
- },
- {
- label: "累计消费",
- prop: "totalConsumption",
- sortable: "custom",
- templet: "custom",
- slotName: "totalConsumption",
- },
- { label: "创建时间", prop: "createTime" },
- { label: "所属企业", prop: "firmName" },
- {
- label: "操作",
- prop: "operation",
- templet: "tool",
- operat: [
- {
- name: "detail",
- text: "详情",
- attrs: {
- type: "primary",
- icon: "view",
- link: true,
- size: "small",
- },
- },
- {
- name: "refundRecord",
- text: "退款记录",
- attrs: {
- type: "info",
- link: true,
- size: "small",
- },
- },
- {
- name: "refund",
- text: "退款",
- attrs: {
- type: "danger",
- size: "small",
- },
- render: (row: any) => row.balance > 0,
- },
- ],
- },
- ],
- });
- // 详情配置
- const editModalConfig: IModalConfig<UserInfoForm> = reactive({
- permPrefix: "business:user-info",
- component: "drawer",
- drawer: {
- title: "用户详情",
- size: 500,
- },
- pk: "id",
- formItems: [
- {
- type: "input",
- attrs: {
- placeholder: "用户ID",
- disabled: true,
- },
- label: "用户ID",
- prop: "id",
- },
- {
- type: "input",
- attrs: {
- placeholder: "手机号",
- disabled: true,
- },
- label: "手机号",
- prop: "phone",
- },
- {
- type: "input",
- attrs: {
- placeholder: "昵称",
- disabled: true,
- },
- label: "昵称",
- prop: "nickName",
- },
- {
- type: "input",
- attrs: {
- placeholder: "微信openid",
- disabled: true,
- },
- label: "微信openid",
- prop: "openid",
- },
- ],
- });
- // 处理操作按钮点击
- const handleOperateClick = (data: IObject) => {
- if (data.name === "detail") {
- handleEditClick(data.row, async () => {
- return await UserInfoAPI.getFormData(data.row.id);
- });
- } else if (data.name === "refundRecord") {
- // 打开退款记录弹窗
- openRefundRecordDialog(data.row);
- } else if (data.name === "refund") {
- ElMessageBox.confirm("确认要为该用户执行退款操作吗?", "退款确认", {
- confirmButtonText: "确认",
- cancelButtonText: "取消",
- type: "warning",
- beforeClose: async (action, instance, done) => {
- if (action === "confirm") {
- instance.confirmButtonLoading = true;
- instance.confirmButtonText = "退款中...";
- try {
- await UserInfoAPI.refund(data.row.id);
- ElMessage.success("退款成功");
- done();
- contentRef.value?.fetchPageData();
- } catch (error) {
- instance.confirmButtonLoading = false;
- instance.confirmButtonText = "确认";
- }
- } else {
- done();
- }
- },
- });
- }
- };
- // ==================== 导出日期区间弹窗相关 ====================
- const exportDateDialogVisible = ref(false);
- const exportDateRange = ref<[string, string] | null>(null);
- const exportBtnLoading = ref(false);
- // 覆盖 usePage 默认的 handleExportClick,先弹窗选择日期
- const handleExportClick = () => {
- exportDateRange.value = null;
- exportDateDialogVisible.value = true;
- };
- // 确认导出
- const handleExportConfirm = () => {
- const queryParams = searchRef.value?.getQueryParams() ?? {};
- const filteredQuery: Record<string, any> = {};
- for (const key in queryParams) {
- const value = queryParams[key];
- if (value !== "" && value !== null && value !== undefined) {
- filteredQuery[key] = value;
- }
- }
- if (exportDateRange.value && exportDateRange.value.length === 2) {
- filteredQuery.startTime = `${exportDateRange.value[0]} 00:00:00`;
- filteredQuery.endTime = `${exportDateRange.value[1]} 23:59:59`;
- }
- exportBtnLoading.value = true;
- contentRef.value?.exportPageData(filteredQuery);
- // exportPageData 内部异步处理,此处延迟关闭弹窗
- setTimeout(() => {
- exportBtnLoading.value = false;
- exportDateDialogVisible.value = false;
- }, 1500);
- };
- // 处理工具栏按钮点击
- const handleToolbarClick = (name: string) => {
- console.log(name);
- };
- // 排序参数
- const sortParams = reactive({
- sortField: "",
- sortOrder: "",
- });
- // 处理排序变化
- const handleSortChange = (data: { prop: string; order: string | null }) => {
- sortParams.sortField = data.prop || "";
- sortParams.sortOrder = data.order || "";
- // 带着排序参数重新查询
- contentRef.value?.fetchPageData({ ...sortParams });
- };
- // ==================== 退款记录弹窗相关 ====================
- const refundRecordDialogVisible = ref(false);
- const refundTableLoading = ref(false);
- const refundTableData = ref<UserRefundOrderPageVO[]>([]);
- const refundTotal = ref(0);
- const refundDateRange = ref<[string, string]>();
- const currentUserId = ref<number>(0);
- // 退款查询参数
- const refundQueryParams = reactive<UserRefundOrderPageQuery>({
- pageNum: 1,
- pageSize: 10,
- userId: 0,
- startTime: undefined,
- endTime: undefined,
- });
- // 打开退款记录弹窗
- const openRefundRecordDialog = (row: any) => {
- currentUserId.value = row.id;
- refundQueryParams.userId = row.id;
- refundQueryParams.pageNum = 1;
- refundQueryParams.startTime = undefined;
- refundQueryParams.endTime = undefined;
- refundDateRange.value = undefined;
- refundRecordDialogVisible.value = true;
- fetchRefundRecordList();
- };
- // 获取退款记录列表
- const fetchRefundRecordList = async () => {
- refundTableLoading.value = true;
- try {
- const response = await UserInfoAPI.getUserRefundOrderList(refundQueryParams);
- refundTableData.value = response.list;
- refundTotal.value = response.total;
- } catch (error) {
- console.error("获取退款记录失败:", error);
- ElMessage.error("获取退款记录失败");
- } finally {
- refundTableLoading.value = false;
- }
- };
- // 处理日期范围变化
- const handleRefundDateChange = (value: [string, string] | null) => {
- if (value && value.length === 2) {
- refundQueryParams.startTime = value[0];
- refundQueryParams.endTime = value[1];
- } else {
- refundQueryParams.startTime = undefined;
- refundQueryParams.endTime = undefined;
- }
- };
- // 查询退款记录
- const handleRefundQuery = () => {
- refundQueryParams.pageNum = 1;
- fetchRefundRecordList();
- };
- // 重置退款查询
- const handleRefundReset = () => {
- refundQueryParams.pageNum = 1;
- refundQueryParams.startTime = undefined;
- refundQueryParams.endTime = undefined;
- refundDateRange.value = undefined;
- fetchRefundRecordList();
- };
- // 分页大小变化
- const handleRefundSizeChange = (size: number) => {
- refundQueryParams.pageSize = size;
- refundQueryParams.pageNum = 1;
- fetchRefundRecordList();
- };
- // 当前页变化
- const handleRefundCurrentChange = (page: number) => {
- refundQueryParams.pageNum = page;
- fetchRefundRecordList();
- };
- // ==================== 退款日志抽屉相关 ====================
- const refundLogDrawerVisible = ref(false);
- const currentNotifyRequest = ref("");
- // 处理退款记录点击
- const handleRefundRecordClick = (row: UserRefundOrderPageVO) => {
- currentNotifyRequest.value = row.notifyRequest || "";
- refundLogDrawerVisible.value = true;
- };
- </script>
- <style scoped>
- .money-text {
- font-weight: bold;
- font-size: 13px;
- }
- .money-text.primary {
- color: #409eff;
- }
- .money-text.success {
- color: #67c23a;
- }
- .money-text.danger {
- color: #f56c6c;
- }
- .money-text.warning {
- color: #e6a23c;
- }
- .refund-record-container {
- padding: 0;
- }
- .search-form {
- margin-bottom: 16px;
- }
- .el-pagination {
- margin-top: 16px;
- justify-content: flex-end;
- }
- </style>
|