Files
smartDriveEEFront/src/views/lb-business/daily-report/index.vue
2026-04-25 22:37:34 +08:00

691 lines
25 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-card class="filter-container" shadow="never">
<el-form :model="queryParams" :inline="true" label-width="100px">
<el-form-item label="昵称">
<el-input v-model="queryParams.nickname" clearable placeholder="请输入昵称" style="width: 180px" />
</el-form-item>
<el-form-item label="数据类型">
<el-select v-model="queryParams.dataType" clearable placeholder="请选择数据类型" style="width: 180px">
<el-option label="详情" value="report_detail" />
<el-option label="汇总" value="report_sum" />
</el-select>
</el-form-item>
<el-form-item label="报表开始">
<el-date-picker
v-model="queryParams.reportStartTime"
type="datetime"
clearable
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="报表开始时间"
style="width: 200px"
/>
</el-form-item>
<el-form-item label="报表结束">
<el-date-picker
v-model="queryParams.reportEndTime"
type="datetime"
clearable
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="报表结束时间"
style="width: 200px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
<el-button icon="el-icon-delete" @click="resetQuery">清空</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="action-container" shadow="never">
<div class="action-bar">
<div class="action-buttons">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button>
<el-button type="warning" icon="el-icon-data-analysis" :loading="reportSumLoading" @click="openReportSumDialog">汇总数据</el-button>
<el-button type="danger" icon="el-icon-delete" :loading="deleteByDateLoading" @click="openDeleteByDateDialog">删除报表数据</el-button>
<el-button type="success" icon="el-icon-download" :loading="exportLoading" @click="openExportDialog">报表导出</el-button>
</div>
<div class="action-tip">
说明服务费=当日买入×0.01差额=当日卖出当日买入应收应付=差额服务费>0 应收当前人<0 应付当前人
</div>
</div>
</el-card>
<el-card class="table-container" shadow="never">
<el-table v-loading="loading" :data="list" style="width: 100%">
<el-table-column label="报表日期" prop="reportDate" min-width="170">
<template slot-scope="scope">
{{ formatDate(scope.row.reportDate) }}
</template>
</el-table-column>
<el-table-column label="昵称" prop="nickname" min-width="120" />
<el-table-column label="昨日买入金额" prop="yestodayBuyAmt" min-width="130" />
<el-table-column label="当日卖出金额" prop="dailySellAmt" min-width="130" />
<el-table-column label="当日买入金额" prop="dailyBuyAmt" min-width="130" />
<el-table-column label="服务费" prop="serviceAmt" min-width="100" />
<el-table-column label="差额" prop="diffAmt" min-width="100" />
<el-table-column label="应收应付" prop="actualReceiptsPayments" min-width="110" />
<el-table-column label="抵扣金额" prop="dikouAmt" min-width="110" />
<el-table-column label="数据类型" prop="dataType" min-width="100" />
<el-table-column label="描述" prop="descContent" min-width="140" show-overflow-tooltip />
<el-table-column label="创建时间" prop="createdAt" min-width="170">
<template slot-scope="scope">
{{ formatDateTime(scope.row.createdAt) }}
</template>
</el-table-column>
<el-table-column label="更新时间" prop="updatedAt" min-width="170">
<template slot-scope="scope">
{{ formatDateTime(scope.row.updatedAt) }}
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="140">
<template slot-scope="scope">
<el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" style="color: #F56C6C;" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="queryParams.current"
:page-size="queryParams.size"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="760px" @close="resetForm">
<el-form ref="dataForm" :model="form" :rules="formRules" label-width="120px">
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="租户ID" prop="tenantId">
<el-input v-model="form.tenantId" placeholder="请输入租户ID" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="用户ID" prop="userId">
<el-input v-model="form.userId" placeholder="请输入用户ID" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="昵称" prop="nickname">
<el-input v-model="form.nickname" placeholder="请输入昵称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="数据类型" prop="dataType">
<el-input v-model="form.dataType" placeholder="请输入数据类型" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="昨日买入金额" prop="yestodayBuyAmt">
<el-input v-model="form.yestodayBuyAmt" placeholder="请输入昨日买入金额" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="当日卖出金额" prop="dailySellAmt">
<el-input v-model="form.dailySellAmt" placeholder="请输入当日卖出金额" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="当日买入金额" prop="dailyBuyAmt">
<el-input v-model="form.dailyBuyAmt" placeholder="请输入当日买入金额" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="服务费" prop="serviceAmt">
<el-input v-model="form.serviceAmt" placeholder="请输入服务费" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="抵扣金额" prop="dikouAmt">
<el-input v-model="form.dikouAmt" placeholder="请输入抵扣金额" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="差额" prop="diffAmt">
<el-input v-model="form.diffAmt" placeholder="请输入差额" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="报表日期" prop="reportDate">
<el-date-picker
v-model="form.reportDate"
type="datetime"
clearable
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择报表日期"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="描述" prop="descContent">
<el-input v-model="form.descContent" type="textarea" :rows="3" placeholder="请输入描述" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
</div>
</el-dialog>
<el-dialog title="删除报表数据" :visible.sync="deleteByDateDialogVisible" width="480px" @close="resetDeleteByDateForm">
<el-form ref="deleteByDateFormRef" :model="deleteByDateForm" :rules="deleteByDateRules" label-width="120px">
<el-form-item label="报表日期" prop="reportDate">
<el-date-picker
v-model="deleteByDateForm.reportDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择要删除的报表日期"
style="width: 100%"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="deleteByDateDialogVisible = false">取消</el-button>
<el-button type="danger" :loading="deleteByDateLoading" @click="submitDeleteByDate">确定删除</el-button>
</div>
</el-dialog>
<el-dialog title="汇总数据" :visible.sync="reportSumDialogVisible" width="480px" @close="resetReportSumForm">
<el-form ref="reportSumFormRef" :model="reportSumForm" :rules="reportSumRules" label-width="120px">
<el-form-item label="报表日期" prop="reportDate">
<el-date-picker
v-model="reportSumForm.reportDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择报表日期"
style="width: 100%"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="reportSumDialogVisible = false">取消</el-button>
<el-button type="warning" :loading="reportSumLoading" @click="submitReportSum">确定汇总</el-button>
</div>
</el-dialog>
<el-dialog title="报表导出" :visible.sync="exportDialogVisible" width="480px" @close="resetExportForm">
<el-form ref="exportFormRef" :model="exportForm" :rules="exportRules" label-width="120px">
<el-form-item label="报表日期" prop="reportDate">
<el-date-picker
v-model="exportForm.reportDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择要导出的报表日期"
style="width: 100%"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="exportDialogVisible = false">取消</el-button>
<el-button type="success" :loading="exportLoading" @click="submitExport">确定导出</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
addLbDailyUserTradeReport,
calculateReportSumByDateAndTenant,
deleteLbDailyUserTradeReportByDateAndTenant,
deleteLbDailyUserTradeReport,
exportLbDailyUserTradeReportByDateAndTenant,
getLbDailyUserTradeReportList,
updateLbDailyUserTradeReport
} from '@/api/lb-daily-user-trade-report'
import { getBusinessHeaders } from '@/utils/business-headers'
function formatDateTimeForQuery(date) {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day} 00:00:00`
}
function getDefaultReportRange() {
const today = new Date()
const yesterday = new Date(today)
yesterday.setDate(yesterday.getDate() - 1)
return {
reportStartTime: formatDateTimeForQuery(yesterday),
reportEndTime: formatDateTimeForQuery(today)
}
}
function getLocalTodayDateString() {
const date = new Date()
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
export default {
name: 'LbDailyReport',
data() {
return {
loading: false,
submitLoading: false,
deleteByDateLoading: false,
reportSumLoading: false,
exportLoading: false,
list: [],
total: 0,
dialogVisible: false,
deleteByDateDialogVisible: false,
reportSumDialogVisible: false,
exportDialogVisible: false,
dialogTitle: '',
isEdit: false,
queryParams: {
current: 1,
size: 10,
dataType: '',
nickname: '',
...getDefaultReportRange()
},
form: {
id: '',
tenantId: '',
userId: '',
nickname: '',
yestodayBuyAmt: '',
dailySellAmt: '',
dailyBuyAmt: '',
serviceAmt: '',
diffAmt: '',
dikouAmt: '',
dataType: '',
reportDate: '',
descContent: ''
},
formRules: {
tenantId: [{ required: true, message: '请输入租户ID', trigger: 'blur' }],
userId: [{ required: true, message: '请输入用户ID', trigger: 'blur' }]
},
deleteByDateForm: {
reportDate: ''
},
deleteByDateRules: {
reportDate: [{ required: true, message: '请选择报表日期', trigger: 'change' }]
},
reportSumForm: {
reportDate: getLocalTodayDateString()
},
reportSumRules: {
reportDate: [{ required: true, message: '请选择报表日期', trigger: 'change' }]
},
exportForm: {
reportDate: getLocalTodayDateString()
},
exportRules: {
reportDate: [{ required: true, message: '请选择报表日期', trigger: 'change' }]
}
}
},
created() {
this.fetchList()
},
methods: {
fetchList() {
this.loading = true
getLbDailyUserTradeReportList(this.buildListParams())
.then((res) => {
if (res && (res.code === 20000 || res.code === 200)) {
this.list = Array.isArray(res.data) ? res.data : []
this.total = typeof res.total === 'number' ? res.total : 0
} else {
this.list = []
this.total = 0
}
})
.catch(() => {
this.list = []
this.total = 0
})
.finally(() => {
this.loading = false
})
},
buildListParams() {
const params = {
current: this.queryParams.current,
size: this.queryParams.size,
dataType: this.queryParams.dataType || undefined,
nickname: this.queryParams.nickname || undefined,
reportStartTime: this.queryParams.reportStartTime || undefined,
reportEndTime: this.queryParams.reportEndTime || undefined
}
Object.keys(params).forEach((key) => {
if (params[key] === undefined || params[key] === '') delete params[key]
})
return params
},
handleQuery() {
this.queryParams.current = 1
this.fetchList()
},
resetQuery() {
this.queryParams = {
current: 1,
size: 10,
dataType: '',
nickname: '',
...getDefaultReportRange()
}
this.fetchList()
},
handleSizeChange(size) {
this.queryParams.size = size
this.fetchList()
},
handleCurrentChange(current) {
this.queryParams.current = current
this.fetchList()
},
handleAdd() {
this.dialogTitle = '新增当天报表'
this.isEdit = false
this.resetFormData()
this.dialogVisible = true
this.$nextTick(() => {
this.$refs.dataForm && this.$refs.dataForm.clearValidate()
})
},
handleEdit(row) {
this.dialogTitle = '编辑当天报表'
this.isEdit = true
this.form = {
id: row.id,
tenantId: row.tenantId || '',
userId: row.userId || '',
nickname: row.nickname || '',
yestodayBuyAmt: this.toInputString(row.yestodayBuyAmt),
dailySellAmt: this.toInputString(row.dailySellAmt),
dailyBuyAmt: this.toInputString(row.dailyBuyAmt),
serviceAmt: this.toInputString(row.serviceAmt),
diffAmt: this.toInputString(row.diffAmt),
dikouAmt: this.toInputString(row.dikouAmt),
dataType: row.dataType || '',
reportDate: this.normalizeDateTime(row.reportDate),
descContent: row.descContent || ''
}
this.dialogVisible = true
this.$nextTick(() => {
this.$refs.dataForm && this.$refs.dataForm.clearValidate()
})
},
handleDelete(row) {
this.$confirm('确定删除这条当天报表记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return deleteLbDailyUserTradeReport(row.id)
}).then((res) => {
this.$message.success(res.message || '删除成功')
this.fetchList()
}).catch(() => {})
},
openDeleteByDateDialog() {
this.deleteByDateDialogVisible = true
this.$nextTick(() => {
if (this.$refs.deleteByDateFormRef) this.$refs.deleteByDateFormRef.clearValidate()
})
},
openReportSumDialog() {
this.reportSumForm.reportDate = getLocalTodayDateString()
this.reportSumDialogVisible = true
this.$nextTick(() => {
if (this.$refs.reportSumFormRef) this.$refs.reportSumFormRef.clearValidate()
})
},
openExportDialog() {
this.exportForm.reportDate = getLocalTodayDateString()
this.exportDialogVisible = true
this.$nextTick(() => {
if (this.$refs.exportFormRef) this.$refs.exportFormRef.clearValidate()
})
},
submitDeleteByDate() {
this.$refs.deleteByDateFormRef.validate((valid) => {
if (!valid) return
const tenantId = this.getTenantIdFromBusinessHeaders()
if (!tenantId) {
this.$message.error('未获取到租户ID请先登录或切换到正确租户后重试')
return
}
this.$confirm(`确认删除报表日期为 ${this.deleteByDateForm.reportDate} 的报表数据吗?`, '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.deleteByDateLoading = true
return deleteLbDailyUserTradeReportByDateAndTenant(this.deleteByDateForm.reportDate, tenantId)
}).then((res) => {
this.$message.success(res.message || '删除成功')
this.deleteByDateDialogVisible = false
this.fetchList()
}).finally(() => {
this.deleteByDateLoading = false
}).catch(() => {})
})
},
resetDeleteByDateForm() {
if (this.$refs.deleteByDateFormRef) this.$refs.deleteByDateFormRef.resetFields()
this.deleteByDateForm = {
reportDate: ''
}
},
submitReportSum() {
this.$refs.reportSumFormRef.validate((valid) => {
if (!valid) return
const tenantId = this.getTenantIdFromBusinessHeaders()
if (!tenantId) {
this.$message.error('未获取到租户ID请先登录或切换到正确租户后重试')
return
}
this.reportSumLoading = true
calculateReportSumByDateAndTenant(this.reportSumForm.reportDate, tenantId)
.then((res) => {
this.$message.success(res.message || '汇总成功')
this.reportSumDialogVisible = false
this.fetchList()
})
.finally(() => {
this.reportSumLoading = false
})
})
},
resetReportSumForm() {
if (this.$refs.reportSumFormRef) this.$refs.reportSumFormRef.resetFields()
this.reportSumForm = {
reportDate: getLocalTodayDateString()
}
},
submitExport() {
this.$refs.exportFormRef.validate((valid) => {
if (!valid) return
const tenantId = this.getTenantIdFromBusinessHeaders()
if (!tenantId) {
this.$message.error('未获取到租户ID请先登录或切换到正确租户后重试')
return
}
this.exportLoading = true
exportLbDailyUserTradeReportByDateAndTenant(this.exportForm.reportDate, tenantId)
.then((blob) => {
const safeBlob = blob instanceof Blob ? blob : new Blob([blob], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
const url = window.URL.createObjectURL(safeBlob)
const link = document.createElement('a')
link.href = url
link.download = `lbDailyUserTradeReport_${this.exportForm.reportDate}_${tenantId}.xlsx`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
this.$message.success('导出成功')
this.exportDialogVisible = false
})
.finally(() => {
this.exportLoading = false
})
})
},
resetExportForm() {
if (this.$refs.exportFormRef) this.$refs.exportFormRef.resetFields()
this.exportForm = {
reportDate: getLocalTodayDateString()
}
},
getTenantIdFromBusinessHeaders() {
const headers = getBusinessHeaders()
const value = headers && headers['X-Tenant-Id']
return value ? String(value).trim() : ''
},
handleSubmit() {
this.$refs.dataForm.validate((valid) => {
if (!valid) return
const payload = this.buildPayload()
this.submitLoading = true
const req = this.isEdit ? updateLbDailyUserTradeReport(payload) : addLbDailyUserTradeReport(payload)
req.then((res) => {
this.$message.success(res.message || (this.isEdit ? '更新成功' : '新增成功'))
this.dialogVisible = false
this.fetchList()
}).finally(() => {
this.submitLoading = false
})
})
},
buildPayload() {
const payload = {
tenantId: this.form.tenantId,
userId: this.form.userId,
nickname: this.form.nickname || undefined,
yestodayBuyAmt: this.parseDecimal(this.form.yestodayBuyAmt),
dailySellAmt: this.parseDecimal(this.form.dailySellAmt),
dailyBuyAmt: this.parseDecimal(this.form.dailyBuyAmt),
serviceAmt: this.parseDecimal(this.form.serviceAmt),
diffAmt: this.parseDecimal(this.form.diffAmt),
dikouAmt: this.parseDecimal(this.form.dikouAmt),
dataType: this.form.dataType || undefined,
reportDate: this.form.reportDate || undefined,
descContent: this.form.descContent || undefined
}
if (this.isEdit) payload.id = this.form.id
Object.keys(payload).forEach((key) => {
if (payload[key] === undefined || payload[key] === '') delete payload[key]
})
return payload
},
parseDecimal(val) {
if (val === null || val === undefined || String(val).trim() === '') return undefined
return String(val).trim()
},
toInputString(val) {
if (val === null || val === undefined) return ''
return String(val)
},
normalizeDateTime(val) {
if (!val) return ''
return String(val).replace('T', ' ')
},
resetForm() {
this.$refs.dataForm && this.$refs.dataForm.resetFields()
this.resetFormData()
},
resetFormData() {
this.form = {
id: '',
tenantId: '',
userId: '',
nickname: '',
yestodayBuyAmt: '',
dailySellAmt: '',
dailyBuyAmt: '',
serviceAmt: '',
diffAmt: '',
dikouAmt: '',
dataType: '',
reportDate: '',
descContent: ''
}
},
formatDateTime(dateTime) {
if (!dateTime) return '-'
const date = new Date(String(dateTime).replace('T', ' '))
if (Number.isNaN(date.getTime())) return String(dateTime)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
},
formatDate(dateTime) {
if (!dateTime) return '-'
const date = new Date(String(dateTime).replace('T', ' '))
if (Number.isNaN(date.getTime())) return String(dateTime)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
}
}
</script>
<style lang="scss" scoped>
.app-container {
.filter-container {
margin-bottom: 20px;
}
.action-container {
margin-bottom: 20px;
.action-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
.action-buttons {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.action-tip {
color: #909399;
font-size: 12px;
line-height: 18px;
}
}
.table-container {
.el-pagination {
margin-top: 20px;
text-align: right;
}
}
}
</style>