优惠券导出

This commit is contained in:
zhonghua.li
2026-07-01 16:49:00 +08:00
parent 3f34255d9c
commit 5943e69046
7 changed files with 183 additions and 16 deletions

View File

@@ -38,3 +38,20 @@ export function syncCoupons(params) {
params params
}) })
} }
export function monthlyStatistic(params) {
return request({
url: '/lbUserCoupon/monthlyStatistic',
method: 'post',
params
})
}
export function exportLbUserCoupon(params) {
return request({
url: '/lbUserCoupon/export',
method: 'get',
params,
responseType: 'blob'
})
}

View File

@@ -4,7 +4,7 @@ import 'normalize.css/normalize.css' // A modern alternative to CSS resets
import ElementUI from 'element-ui' import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css' import 'element-ui/lib/theme-chalk/index.css'
import locale from 'element-ui/lib/locale/lang/en' // lang i18n import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n
import '@/styles/index.scss' // global css import '@/styles/index.scss' // global css
@@ -31,10 +31,7 @@ if (process.env.NODE_ENV === 'production') {
mockXHR() mockXHR()
} }
// set ElementUI lang to EN
Vue.use(ElementUI, { locale }) Vue.use(ElementUI, { locale })
// 如果想要中文版 element-ui,按如下方式声明
// Vue.use(ElementUI)
Vue.config.productionTip = false Vue.config.productionTip = false

View File

@@ -251,7 +251,7 @@
<el-tab-pane label="扩展配置" name="extra"> <el-tab-pane label="扩展配置" name="extra">
<el-form-item label="扩展 JSON"> <el-form-item label="扩展 JSON">
<el-input v-model="form.extraConfig" type="textarea" :rows="8" placeholder='{"key":"value"}' /> <el-input v-model="form.extraConfig" type="textarea" :rows="8" placeholder="{&quot;key&quot;:&quot;value&quot;}" />
</el-form-item> </el-form-item>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>

View File

@@ -14,6 +14,15 @@
<el-form-item label="上级昵称"> <el-form-item label="上级昵称">
<el-input v-model="queryParams.parentNickname" clearable placeholder="请输入上级昵称" style="width: 160px" /> <el-input v-model="queryParams.parentNickname" clearable placeholder="请输入上级昵称" style="width: 160px" />
</el-form-item> </el-form-item>
<el-form-item label="数据类型">
<el-select v-model="queryParams.dataType" clearable placeholder="请选择数据类型" style="width: 160px">
<el-option label="明细数据" value="data_detail" />
<el-option label="月度统计" value="data_month_static" />
</el-select>
</el-form-item>
<el-form-item label="优惠金额大于">
<el-input-number v-model="queryParams.couponAmount" :min="0" :precision="2" :controls-position="'right'" placeholder="请输入金额" style="width: 160px" />
</el-form-item>
<el-form-item label="优惠日期"> <el-form-item label="优惠日期">
<el-date-picker <el-date-picker
v-model="couponDateRange" v-model="couponDateRange"
@@ -35,6 +44,8 @@
<el-card class="action-container" shadow="never"> <el-card class="action-container" shadow="never">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button> <el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button>
<el-button type="success" icon="el-icon-refresh" :loading="syncLoading" style="margin-left: 10px" @click="openSyncDialog">同步优惠券</el-button> <el-button type="success" icon="el-icon-refresh" :loading="syncLoading" style="margin-left: 10px" @click="openSyncDialog">同步优惠券</el-button>
<el-button type="info" icon="el-icon-data-line" :loading="statLoading" style="margin-left: 10px" @click="openStatDialog">按月统计</el-button>
<el-button type="warning" icon="el-icon-download" :loading="exportLoading" style="margin-left: 10px" @click="handleExport">导出</el-button>
</el-card> </el-card>
<el-card class="table-container" shadow="never"> <el-card class="table-container" shadow="never">
@@ -44,6 +55,11 @@
<el-table-column label="用户昵称" prop="userNickname" min-width="120" show-overflow-tooltip /> <el-table-column label="用户昵称" prop="userNickname" min-width="120" show-overflow-tooltip />
<el-table-column label="上级手机号" prop="parentPhone" min-width="120" show-overflow-tooltip /> <el-table-column label="上级手机号" prop="parentPhone" min-width="120" show-overflow-tooltip />
<el-table-column label="上级昵称" prop="parentNickname" min-width="120" show-overflow-tooltip /> <el-table-column label="上级昵称" prop="parentNickname" min-width="120" show-overflow-tooltip />
<el-table-column label="数据类型" prop="dataType" min-width="100" align="center">
<template slot-scope="scope">
{{ scope.row.dataType === 'data_detail' ? '明细数据' : scope.row.dataType === 'data_month_static' ? '月度统计' : '-' }}
</template>
</el-table-column>
<el-table-column label="优惠券金额" prop="couponAmount" min-width="100" align="center"> <el-table-column label="优惠券金额" prop="couponAmount" min-width="100" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.couponAmount != null ? scope.row.couponAmount : '-' }} {{ scope.row.couponAmount != null ? scope.row.couponAmount : '-' }}
@@ -141,6 +157,46 @@
<el-button type="primary" :loading="syncLoading" @click="handleSync">确定</el-button> <el-button type="primary" :loading="syncLoading" @click="handleSync">确定</el-button>
</div> </div>
</el-dialog> </el-dialog>
<el-dialog title="按月统计" :visible.sync="statDialogVisible" width="600px" @close="resetStatForm">
<el-form ref="statFormRef" :model="statForm" label-width="100px">
<el-form-item label="租户ID">
<el-input :value="tenantIdDisplay" disabled placeholder="未获取到租户ID" />
</el-form-item>
<el-form-item label="优惠日期范围">
<el-date-picker
v-model="statDateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
style="width: 100%"
/>
</el-form-item>
</el-form>
<div v-if="statResult && statResult.length" style="margin-top: 20px;">
<el-divider content-position="left">统计结果</el-divider>
<el-table :data="statResult" style="width: 100%" size="small">
<el-table-column prop="month" label="月份" min-width="100" />
<el-table-column prop="totalAmount" label="总金额" min-width="120" align="right">
<template slot-scope="scope">
{{ scope.row.totalAmount != null ? scope.row.totalAmount.toFixed(2) : '-' }}
</template>
</el-table-column>
<el-table-column prop="count" label="数量" min-width="100" align="right" />
</el-table>
<div v-if="statTotal" style="margin-top: 10px; text-align: right; font-weight: bold;">
合计金额: {{ statTotal.toFixed(2) }}
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="statDialogVisible = false">关闭</el-button>
<el-button type="primary" :loading="statLoading" @click="handleStat">统计</el-button>
</div>
</el-dialog>
</div> </div>
</template> </template>
@@ -148,7 +204,9 @@
import { import {
addLbUserCoupon, addLbUserCoupon,
deleteLbUserCoupon, deleteLbUserCoupon,
exportLbUserCoupon,
getLbUserCouponList, getLbUserCouponList,
monthlyStatistic,
syncCoupons, syncCoupons,
updateLbUserCoupon updateLbUserCoupon
} from '@/api/lb-user-coupon' } from '@/api/lb-user-coupon'
@@ -161,6 +219,7 @@ export default {
loading: false, loading: false,
submitLoading: false, submitLoading: false,
syncLoading: false, syncLoading: false,
exportLoading: false,
list: [], list: [],
total: 0, total: 0,
couponDateRange: [], couponDateRange: [],
@@ -174,13 +233,20 @@ export default {
syncFormRules: { syncFormRules: {
recentDays: [{ required: true, message: '请输入最近天数', trigger: 'blur' }] recentDays: [{ required: true, message: '请输入最近天数', trigger: 'blur' }]
}, },
statDialogVisible: false,
statLoading: false,
statDateRange: [],
statResult: [],
statTotal: null,
queryParams: { queryParams: {
current: 1, current: 1,
size: 10, size: 10,
userPhone: '', userPhone: '',
userNickname: '', userNickname: '',
parentPhone: '', parentPhone: '',
parentNickname: '' parentNickname: '',
dataType: '',
couponAmount: 3044
}, },
form: { form: {
id: '', id: '',
@@ -197,14 +263,14 @@ export default {
} }
} }
}, },
created() {
this.fetchList()
},
computed: { computed: {
tenantIdDisplay() { tenantIdDisplay() {
return this.getTenantIdFromBusinessHeaders() return this.getTenantIdFromBusinessHeaders()
} }
}, },
created() {
this.fetchList()
},
methods: { methods: {
getTenantIdFromBusinessHeaders() { getTenantIdFromBusinessHeaders() {
const headers = getBusinessHeaders() const headers = getBusinessHeaders()
@@ -240,7 +306,9 @@ export default {
userPhone: this.queryParams.userPhone || undefined, userPhone: this.queryParams.userPhone || undefined,
userNickname: this.queryParams.userNickname || undefined, userNickname: this.queryParams.userNickname || undefined,
parentPhone: this.queryParams.parentPhone || undefined, parentPhone: this.queryParams.parentPhone || undefined,
parentNickname: this.queryParams.parentNickname || undefined parentNickname: this.queryParams.parentNickname || undefined,
dataType: this.queryParams.dataType || undefined,
couponAmountMin: this.queryParams.couponAmount
} }
const range = this.couponDateRange || [] const range = this.couponDateRange || []
if (range[0]) params.couponDateStart = range[0] if (range[0]) params.couponDateStart = range[0]
@@ -261,7 +329,9 @@ export default {
userPhone: '', userPhone: '',
userNickname: '', userNickname: '',
parentPhone: '', parentPhone: '',
parentNickname: '' parentNickname: '',
dataType: '',
couponAmount: undefined
} }
this.couponDateRange = [] this.couponDateRange = []
this.fetchList() this.fetchList()
@@ -408,6 +478,89 @@ export default {
}) })
}) })
}, },
openStatDialog() {
this.statDialogVisible = true
},
resetStatForm() {
this.statDateRange = []
this.statResult = []
this.statTotal = null
},
handleStat() {
const tenantId = this.getTenantIdFromBusinessHeaders()
if (!tenantId) {
this.$message.error('未获取到租户ID,请先登录或配置业务请求头中的租户')
return
}
this.statLoading = true
const params = { tenantId }
const range = this.statDateRange || []
if (range[0]) params.couponDateStart = range[0]
if (range[1]) params.couponDateEnd = range[1]
monthlyStatistic(params)
.then((res) => {
if (res && (res.success === true || res.code === 200 || res.code === 20000)) {
this.statResult = Array.isArray(res.data) ? res.data : []
this.statTotal = this.statResult.reduce((sum, item) => {
const amount = typeof item.totalAmount === 'number' ? item.totalAmount : 0
return sum + amount
}, 0)
if (!this.statResult.length) {
this.$message.info('该时间段内暂无统计数据')
}
} else {
this.$message.error((res && (res.message || res.msg)) || '统计失败')
}
})
.catch(() => {
this.$message.error('统计失败')
})
.finally(() => {
this.statLoading = false
})
},
handleExport() {
const tenantId = this.getTenantIdFromBusinessHeaders()
if (!tenantId) {
this.$message.error('未获取到租户ID,请先登录或配置业务请求头中的租户')
return
}
this.exportLoading = true
const params = {
tenantId,
userPhone: this.queryParams.userPhone || undefined,
userNickname: this.queryParams.userNickname || undefined,
parentPhone: this.queryParams.parentPhone || undefined,
parentNickname: this.queryParams.parentNickname || undefined,
dataType: this.queryParams.dataType || undefined,
couponAmountMin: this.queryParams.couponAmount
}
const range = this.couponDateRange || []
if (range[0]) params.couponDateStart = range[0]
if (range[1]) params.couponDateEnd = range[1]
Object.keys(params).forEach((key) => {
if (params[key] === undefined || params[key] === '') delete params[key]
})
exportLbUserCoupon(params)
.then((res) => {
const blob = new Blob([res], { type: 'application/vnd.ms-excel' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = '用户优惠券.xlsx'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
this.$message.success('导出成功')
})
.catch(() => {
this.$message.error('导出失败')
})
.finally(() => {
this.exportLoading = false
})
},
formatDateTime(dateTime) { formatDateTime(dateTime) {
if (!dateTime) return '-' if (!dateTime) return '-'
const dateStr = String(dateTime).replace('T', ' ') const dateStr = String(dateTime).replace('T', ' ')

View File

@@ -165,8 +165,8 @@
type="date" type="date"
placeholder="可选,格式 yyyy-MM-dd" placeholder="可选,格式 yyyy-MM-dd"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd"
@change="handleImportDateChange"
style="width: 100%" style="width: 100%"
@change="handleImportDateChange"
/> />
</el-form-item> </el-form-item>
</el-form> </el-form>

View File

@@ -480,14 +480,14 @@ export default {
} }
} }
}, },
created() {
this.fetchList()
},
computed: { computed: {
currentLoginTenantIdDisplay() { currentLoginTenantIdDisplay() {
return this.getTenantIdFromBusinessHeaders() return this.getTenantIdFromBusinessHeaders()
} }
}, },
created() {
this.fetchList()
},
methods: { methods: {
getTenantIdFromBusinessHeaders() { getTenantIdFromBusinessHeaders() {
const headers = getBusinessHeaders() const headers = getBusinessHeaders()