13123
This commit is contained in:
1 parent
8d9df8ffc0
commit
1e78cc6d12
3 files changed
+288
-1
No files matched your search
+1
-1
@@ -3,4 +3,4 @@ module.exports = {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user