This commit is contained in:
dzc committed 2026-08-31 11:29:53 +08:00
1 parent 8d9df8ffc0
commit 1e78cc6d12
3 files changed
+288 -1

No files matched your search

+1 -1
View File
@@ -3,4 +3,4 @@ module.exports = {
tailwindcss: {},
autoprefixer: {},
},
};
}
+27
View File
@@ -35,6 +35,24 @@ export function pageTh(query?: any) {
})
}
/**
* 按管理端当前筛选条件分页查询全部类型的钱包流水。
*
* @param query 分页、用户、部门、身份、收支方向、交易类型和流水时间等筛选条件
* @returns 后端统一的钱包流水分页响应
*/
export function pageAll(query?: any) {
return request({
url: "/wallet/wallet-transaction/page-all",
method: "post",
params: {
current: query.current,
size: query.size,
},
data: query,
})
}
export function thTotal(data?: any) {
return request({
url: "/wallet/wallet-transaction/th-total",
@@ -152,6 +170,15 @@ export function submitCzExport(query: Record<string, unknown>) {
return request<unknown, WalletTransactionExportTaskResponse>({ url: "/wallet/wallet-transaction/export-cz-async", method: "post", data: query })
}
/** 提交全部类型钱包流水异步分片导出任务。 */
export function submitAllExport(query: Record<string, unknown>) {
return request<unknown, WalletTransactionExportTaskResponse>({
url: "/wallet/wallet-transaction/export-all-async",
method: "post",
data: query,
})
}
/** 提交记账扣款异步分片导出任务。 */
export function submitJzExport(query: Record<string, unknown>) {
return request<unknown, WalletTransactionExportTaskResponse>({ url: "/wallet/wallet-transaction/export-jz-async", method: "post", data: query })
@@ -0,0 +1,260 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<DraggableDepTree
ref="draggableDepTreeRef"
placeholder="请输入部门名称"
:show-expand="true"
@getSelectNode="handleGetSelectNode"
switch-inactive-text="包含"
switch-active-text="不包含"
:defaultTreeWidth="20"
:nodeClickable="false"
>
<template #default>
<SearchPanel
v-show="showSearch"
ref="queryRef"
:model="state.queryForm"
:loading="Boolean(state.loading)"
:search-text="$t('common.queryBtn')"
:reset-text="$t('common.resetBtn')"
@search="getDataList"
@reset="resetQuery"
>
<el-form-item style="width: 300px" label="用户" prop="userId">
<select-user v-model="state.queryForm.userId" />
</el-form-item>
<el-form-item style="width: 220px" label="交易类型" prop="bizType">
<dict-select v-model="state.queryForm.bizType" dict-code="wallet_biz" />
</el-form-item>
<el-form-item style="width: 200px" label="收支方向" prop="type">
<el-select v-model="state.queryForm.type" clearable placeholder="全部">
<el-option v-for="option in transactionTypeOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item style="width: 300px" label="流水时间" prop="createTimeList">
<el-date-picker
v-model="state.queryForm.createTimeList"
value-format="YYYY-MM-DD"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
<template #more>
<user-identity-query
v-model:identity-types="state.queryForm.identityTypes"
v-model:student-info="state.queryForm.studentInfo"
v-model:teacher-info="state.queryForm.teacherInfo"
v-model:has-face="state.queryForm.hasFace"
/>
<el-form-item style="width: 260px" label="操作标签" prop="tagList">
<dict-select v-model="state.queryForm.tagList" dict-code="wallet_transaction_tag" multiple />
</el-form-item>
</template>
</SearchPanel>
<el-row>
<div class="mb8" style="width: 100%">
<right-toolbar
:search="false"
v-model:showSearch="showSearch"
:export="'wallet_walletTransaction_export'"
@exportExcel="exportExcel"
class="ml10 mr20"
style="float: right"
@queryTable="getDataList"
/>
</div>
</el-row>
<el-table
:data="state.dataList"
v-loading="state.loading"
border
show-summary
:summary-method="getSummaries"
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@sort-change="sortChangeHandle"
>
<el-table-column type="index" label="#" width="60" />
<el-table-column label="用户信息" min-width="280">
<template #default="{ row }">
<user-info-cell :user="row" :show-gender="false" />
</template>
</el-table-column>
<el-table-column label="身份信息" min-width="320">
<template #default="{ row }">
<user-identity-info :user="row" />
</template>
</el-table-column>
<el-table-column prop="walletTypeUniqueCode" width="200" label="钱包类型">
<template #default="{ row }">
<div class="flex items-center justify-center">
<div class="color-primary">{{ row.walletTypeName }}</div>
<div class="flex items-center ml5">
<dict-tag :options="wallet_type" :value="row.walletTypeUniqueCode" />
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="bizType" width="180" label="交易类型">
<template #default="{ row }">
<dict-tag :options="wallet_biz" :value="row.bizType" />
</template>
</el-table-column>
<el-table-column prop="title" label="流水标题" min-width="180" show-overflow-tooltip />
<el-table-column prop="price" width="160" align="center" label="交易金额">
<template #default="{ row }">
<span :class="getPriceClass(row.price)">{{ displayPrice(row.price) }}</span>
</template>
</el-table-column>
<el-table-column prop="balance" width="160" align="center" label="交易后余额">
<template #default="{ row }">
<span class="color-primary">{{ displayPrice(row.balance) }}</span>
</template>
</el-table-column>
<el-table-column prop="tags" width="200" align="center" label="操作标签">
<template #default="{ row }">
<dict-tag :options="wallet_transaction_tag" :value="splitTags(row.tags)" />
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip />
<el-table-column prop="createTime" width="200" label="流水时间" />
<el-table-column label="操作" width="130" fixed="right" v-auths="['wallet_walletTransaction_edit']">
<template #default="{ row }">
<el-button icon="EditPen" link type="primary" @click="remarkFormRef?.openDialog(row)">修改备注</el-button>
</template>
</el-table-column>
</el-table>
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</template>
</DraggableDepTree>
</div>
<transaction-remark-form ref="remarkFormRef" @refresh="getDataList(false)" />
<async-task-progress-component
ref="exportProgressRef"
:config="exportTaskConfig"
@success="handleExportSuccess"
@failure="handleExportFailure"
/>
</div>
</template>
<script setup lang="ts" name="systemWalletTransaction">
import type { BasicTableProps } from "/@/hooks/table"
import { useTable } from "/@/hooks/table"
import { pageAll, submitAllExport } from "/@/api/wallet/wallet-transaction"
import { useDict } from "/@/hooks/dict"
import { displayPrice } from "/@/utils/formatMoney"
import AsyncTaskProgressComponent from "/@/components/AsyncTaskProgress.vue"
import { createEmptyUserIdentityStudentQuery, createEmptyUserIdentityTeacherQuery } from "/@/components/UserIdentityQuery/types"
import { useWalletUserExport } from "../composables/useWalletUserExport"
/** 所有钱包流水列表共用的备注编辑表单。 */
const TransactionRemarkForm = defineAsyncComponent(() => import("../components/transaction-remark-form.vue"))
/** 收支方向选项;值与后端 WalletTransactionDto 的查询协议保持一致。 */
const transactionTypeOptions = [
{ label: "收入", value: 1 },
{ label: "支出", value: 2 },
]
/** 页面展示交易类型、钱包类型和操作标签所需的字典数据。 */
const { wallet_type, wallet_biz, wallet_transaction_tag } = useDict("wallet_type", "wallet_biz", "wallet_transaction_tag")
/** 搜索面板引用,用于执行 Element Plus 表单重置。 */
const queryRef = ref()
/** 部门树引用,用于重置已选部门和包含模式。 */
const draggableDepTreeRef = ref()
/** 流水备注编辑弹窗引用。 */
const remarkFormRef = ref()
/** 搜索区域显示状态,由右侧工具栏控制。 */
const showSearch = ref(true)
/** 全部钱包流水的分页数据及筛选条件。 */
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {
identityTypes: [],
studentInfo: createEmptyUserIdentityStudentQuery(),
teacherInfo: createEmptyUserIdentityTeacherQuery(),
hasFace: "",
tagList: [],
},
pageList: pageAll,
})
/** 表格分页、查询、排序和项目统一样式能力。 */
const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, tableStyle } = useTable(state)
/** 全部钱包流水导出复用统一提交互斥、进度轮询和文件下载流程。 */
const { exportProgressRef, exportTaskConfig, submitExport, handleExportSuccess, handleExportFailure } = useWalletUserExport({
redisKey: "wallet:user:export:all-transaction",
taskName: "钱包流水导出",
fallbackFileName: "钱包流水.xlsx",
submitTask: submitAllExport,
})
/** 清空表单、部门树条件并从第一页重新查询全部钱包流水。 */
const resetQuery = () => {
queryRef.value?.resetFields()
state.queryForm.deptId = ""
state.queryForm.deptIds = []
draggableDepTreeRef.value?.resetData()
void getDataList()
}
/** 按当前筛选快照提交全部钱包流水异步导出任务,不携带列表页码。 */
const exportExcel = () => {
void submitExport({ ...state.queryForm })
}
/**
* 将流水金额映射为项目既有的收入、支出颜色。
*
* @param price 数据库返回的交易金额,单位为分
* @returns 正数为成功色、负数为危险色、零或空值不额外着色
*/
const getPriceClass = (price?: number) => {
if (price && price > 0) return "color-success"
if (price && price < 0) return "color-danger"
return ""
}
/**
* 将数据库英文逗号分隔的标签转换为字典标签组件需要的数组。
*
* @param tags 流水标签字符串,历史数据允许为空
* @returns 非空标签数组;无标签时返回空数组
*/
const splitTags = (tags?: string) => tags?.split(",").filter(Boolean) ?? []
/** 计算当前页交易金额合计,余额属于逐笔快照因此不参与求和。 */
const getSummaries = ({ columns, data }: { columns: any[]; data: any[] }) => {
const totalRow = Array.from({ length: columns.length })
const priceColumnIndex = columns.findIndex((column: any) => column.property === "price")
if (priceColumnIndex >= 0) {
const totalPrice = data.reduce((total: number, row: any) => total + (Number(row.price) || 0), 0)
totalRow[priceColumnIndex] = displayPrice(totalPrice)
}
return totalRow
}
/**
* 同步部门树选择范围并立即按当前全部条件查询。
*
* @param curClickedNode 当前点击的部门节点
* @param checkedTreeNodes 部门树当前生效的节点集合
* @param isDeepTree 部门树“不包含”开关状态
*/
const handleGetSelectNode = (curClickedNode: any, checkedTreeNodes: any[], isDeepTree: boolean) => {
state.queryForm.deptId = curClickedNode.id
state.queryForm.deptIds = checkedTreeNodes.map((item: any) => item.id)
state.queryForm.isInclude = !isDeepTree
void getDataList()
}
</script>