完善分销功能

This commit is contained in:
gyq
2025-10-28 17:34:58 +08:00
parent 0cafd8651b
commit 2d35ab3b7d
21 changed files with 1656 additions and 92 deletions

View File

@@ -0,0 +1,111 @@
<!-- 增加或者减少余额弹窗 -->
<template>
<el-dialog :title="`余额${form.type == 1 ? '增加' : '扣减'}`" width="400px" v-model="visible" @closed="onClose">
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px" label-position="left">
<el-form-item label="店铺名称">
<el-input v-model="row.shopName" readonly style="width: 300px;"></el-input>
</el-form-item>
<el-form-item :label="`${form.type == 1 ? '充值' : '扣减'}金额`" prop="expense">
<el-input v-model="form.expense" placeholder="请输入金额" style="width: 300px;"
@input="e => form.expense = filterNumberInput(e)">
<template #append></template>
</el-input>
<div>当前余额<span style="color: red;">{{ multiplyAndFormat(row.amount) }}</span></div>
</el-form-item>
<el-form-item label="扣减原因" v-if="form.type == 2" prop="reason">
<el-input type="textarea" :rows="5" v-model="form.reason" placeholder="请输入扣减原因,必填 "></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="submitHandle" :loading="loading"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
import { distributionCashPay } from '@/api/coupon'
import { ElNotification } from 'element-plus'
import { multiplyAndFormat, filterNumberInput } from '@/utils'
const visible = ref(false)
const loading = ref(false)
const formRef = ref(null)
const row = ref({
shopName: '',
money: 0
})
const form = ref({
expense: '',
type: 1,
reason: ''
})
const rules = ref({
expense: [
{
required: true,
message: '请输入充值金额',
triiger: 'blur'
}
],
reason: [
{
required: true,
message: '请输入扣减原因',
triiger: 'blur'
}
]
})
function onClose() {
form.value = {
expense: '',
type: 1,
reason: ''
}
formRef.value.resetFields()
}
const emit = defineEmits(['success'])
function submitHandle() {
formRef.value.validate(async (valid) => {
try {
if (valid) {
loading.value = true
await distributionCashPay({
shopId: row.value.id,
amount: form.value.type == 1 ? form.value.expense : form.value.expense * -1,
remark: form.value.reason
})
emit('success')
visible.value = false
ElNotification({
title: '注意',
message: `${form.value.type == 1 ? '增加' : '扣减'}成功`,
type: 'success'
})
}
} catch (error) {
console.log(error);
}
loading.value = false
})
}
// 显示
function show(obj = {}, type = 1) {
visible.value = true
row.value = { ...obj }
form.value.type = type
}
defineExpose({
show
})
</script>

View File

@@ -0,0 +1,172 @@
<!-- 充值记录 -->
<template>
<el-dialog :title="`记录(${tableRow.shopName}`" width="1200px" v-model="visible">
<el-form label-width="0" inline>
<el-form-item>
<el-date-picker v-model="dateRange" type="datetimerange" range-separator="至" start-placeholder="开始日期时间"
end-placeholder="结束日期时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
@change="timeChange" />
</el-form-item>
<el-form-item>
<el-select v-model="queryForm.type" placeholder="类型" style="width: 200px;">
<el-option :label="item.label" :value="item.value" v-for="item in statusList" :key="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" :loading="tableData.loading" @click="searchHandle">搜索</el-button>
<el-button icon="Refresh" :loading="tableData.loading" @click="resetHandle">重置</el-button>
</el-form-item>
</el-form>
<div class="row">
<el-table :data="tableData.list" stripe border v-loading="tableData.loading" height="500px">
<el-table-column label="类型">
<template #default="scope">
{{statusList.find(item => item.value === scope.row.type).label}}
</template>
</el-table-column>
<el-table-column label="变动金额" prop="expense">
<template #default="scope">
{{ multiplyAndFormat(scope.row.changeAmount || 0) }}
</template>
</el-table-column>
<el-table-column label="变动后金额" prop="balance">
<template #default="scope">
{{ multiplyAndFormat(scope.row.amount || 0) }}
</template>
</el-table-column>
<el-table-column label="变动原因" prop="remark"></el-table-column>
<el-table-column label="关联账单" prop="orderNo"></el-table-column>
<el-table-column label="创建时间" prop="createTime"></el-table-column>
</el-table>
</div>
<div class="row">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
:page-sizes="[10, 20, 50, 100]" background layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</el-dialog>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { distributionShopFlow } from '@/api/coupon'
import { multiplyAndFormat } from '@/utils'
const visible = ref(false)
const statusList = ref([
{
value: '',
label: '全部'
},
{
value: 'manual_recharge',
label: '增加'
},
{
value: 'manual_sub',
label: '减少'
},
{
value: 'self_recharge',
label: '自助充值'
},
{
value: 'refund',
label: '退款'
},
{
value: 'sub',
label: '系统扣减'
}
])
const dateRange = ref([])
const resetQueryForm = ref({
startTime: '',
endTime: '',
type: '',
})
const queryForm = ref({ ...resetQueryForm.value })
function searchHandle() {
tableData.page = 1
getTableData()
}
function resetHandle() {
dateRange.value = []
queryForm.value = { ...resetQueryForm.value }
searchHandle()
}
const tableData = reactive({
loading: false,
list: [],
total: 0,
page: 1,
size: 10
})
// 选择时间
function timeChange(e) {
if (e == null) {
queryForm.value.startTime = ''
queryForm.value.endTime = ''
} else {
queryForm.value.startTime = e[0]
queryForm.value.endTime = e[1]
}
tableData.page = 1
// getTableData()
}
// 分页大小发生变化
function handleSizeChange(e) {
tableData.pageSize = e;
getTableData();
}
// 分页发生变化
function handleCurrentChange(e) {
tableData.page = e;
getTableData();
}
async function getTableData() {
try {
tableData.loading = true
const res = await distributionShopFlow({
shopId: tableRow.value.id,
page: tableData.page,
size: tableData.size,
...queryForm.value
})
tableData.list = res.records
tableData.total = +res.totalRow
} catch (error) {
console.log(error);
}
setTimeout(() => {
tableData.loading = false
}, 500);
}
const tableRow = ref('')
function show(row) {
dateRange.value = []
queryForm.value = { ...resetQueryForm.value }
tableRow.value = { ...row }
visible.value = true
getTableData()
}
defineExpose({
show
})
</script>
<style scoped lang="scss">
.row {
padding-top: 14px;
}
</style>

View File

@@ -0,0 +1,187 @@
<!-- 分销 商家充值 -->
<template>
<div class="gyq_container">
<div class="gyq_content">
<div class="row">
<div class="center">
<el-input v-model="searchValue" style="width: 300px;" placeholder="请输入内容" clearable @clear="getTableData">
<template #prepend>名称</template>
</el-input>
<el-button type="primary" @click="searchHandle">搜索</el-button>
</div>
</div>
<div class="row mt14">
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
<el-table-column prop="name" label="店铺名称" width="300">
<template #default="scope">
<div class="shop_info">
<el-avatar :src="scope.row.logo" shape="square" :size="50"></el-avatar>
<div class="info">
<div class="name">
{{ scope.row.shopName }}
</div>
<div class="tag">
<el-tag effect="dark" type="success" disable-transitions
v-if="scope.row.profiles == 'release'">正式</el-tag>
<el-tag effect="dark" type="warning" disable-transitions
v-if="scope.row.profiles == 'trial'">试用版</el-tag>
</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="type" label="店铺类型">
<template #default="scope">
<div class="column">
<div>{{shopTypeList.find(item => item.value == scope.row.shopType).label}}</div>
<div v-if="scope.row.shopType == 'join' && scope.row.isHeadShop === 0">主店{{ scope.row.headShopName }}
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="money" label="可用余额">
<template #default="scope">
{{ multiplyAndFormat(scope.row.amount || 0) }}
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template #default="scope">
<el-button link type="primary" @click="addBlanceRef.show(scope.row, 1)">充值</el-button>
<el-button link type="primary" @click="addBlanceRef.show(scope.row, 2)">扣减</el-button>
<el-button link type="primary" @click="recordRef.show(scope.row)">查看记录</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="row mt14">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
:page-sizes="[10, 20, 50, 100]" background layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</div>
<addBlance ref="addBlanceRef" @success="getTableData" />
<record ref="recordRef" />
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { shopInfoList } from '@/api/coupon'
import addBlance from '../components/addBlance.vue'
import record from '../components/record.vue'
import { multiplyAndFormat } from '@/utils'
const addBlanceRef = ref(null)
const recordRef = ref(null)
const searchValue = ref('')
function searchHandle() {
tableData.page = 1
getTableData()
}
const shopTypeList = ref([
{
value: 'only',
label: '单店'
},
{
value: 'chain',
label: '连锁店'
},
{
value: 'join',
label: '加盟店'
},
])
const tableData = reactive({
loading: false,
list: [],
total: 0,
page: 1,
size: 10
})
// 分页大小发生变化
function handleSizeChange(e) {
tableData.pageSize = e;
getTableData();
}
// 分页发生变化
function handleCurrentChange(e) {
tableData.page = e;
getTableData();
}
// 店铺列表
async function getTableData() {
try {
tableData.loading = true
const res = await shopInfoList({
page: tableData.page,
size: tableData.size,
shopName: searchValue.value
})
tableData.list = res.records
tableData.total = +res.totalRow
} catch (error) {
console.log(error);
}
setTimeout(() => {
tableData.loading = false
}, 500);
}
onMounted(() => {
getTableData()
})
</script>
<style scoped lang="scss">
.gyq_container {
padding: 14px;
.gyq_content {
padding: 14px;
background-color: #fff;
border-radius: 8px;
}
}
.center {
display: flex;
align-items: center;
gap: 10px;
}
.row {
&.mt14 {
margin-top: 14px;
}
}
.shop_info {
display: flex;
align-items: center;
.info {
padding-left: 10px;
display: flex;
flex-direction: column;
gap: 8px;
.name {
font-size: 16px;
color: #333;
}
}
}
.column {
display: flex;
flex-direction: column;
}
</style>

View File

@@ -0,0 +1,241 @@
<!-- 用户提现记录 -->
<template>
<div class="gyq_container">
<div class="gyq_content">
<div class="title">用户提现记录</div>
<div class="row" style="margin-top: 34px;">
<el-form inline>
<el-form-item>
<el-date-picker style="width: 300px" v-model="times" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD"
@change="selectTimeChange"></el-date-picker>
</el-form-item>
<el-form-item>
<selectUser v-model="queryForm.userId" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" :loading="tableData.loading" @click="searchHandle">搜索</el-button>
<el-button icon="Refresh" :loading="tableData.loading" @click="resetHandle">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="row">
<div class="info_wrap">
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/9.png">
</div>
<div class="info">
<div>提现中</div>
<div>{{ multiplyAndFormat(pending || 0) }}</div>
</div>
</div>
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/10.png">
</div>
<div class="info">
<div>成功提现</div>
<div>{{ multiplyAndFormat(finish || 0) }}</div>
</div>
</div>
</div>
</div>
<div class="row mt14">
<el-table :data="tableData.list" stripe border v-loading="tableData.loading" height="48vh">
<el-table-column label="用户" prop="nickName" width="200">
<template #default="scope">
<div class="column">
<div>{{ scope.row.nickName }}</div>
<div>{{ scope.row.phone }}</div>
</div>
</template>
</el-table-column>
<el-table-column label="提现金额(元)" prop="amount" width="200">
<template #default="scope">
{{ multiplyAndFormat((scope.row.amount + scope.row.serviceFee) || 0) }}
</template>
</el-table-column>
<el-table-column label="手续费(元)" prop="serviceFee">
<template #default="scope">
{{ multiplyAndFormat(scope.row.serviceFee || 0) }}
</template>
</el-table-column>
<el-table-column label="实际到账(元)" prop="amount">
<template #default="scope">
{{ multiplyAndFormat(scope.row.amount || 0) }}
</template></el-table-column>
<el-table-column label="提现时间" prop="createTime"></el-table-column>
<el-table-column label="状态" prop="status">
<template #default="scope">
<el-tag disable-transitions :type="statusList.find(item => item.value == scope.row.status).type">
{{statusList.find(item => item.value == scope.row.status).label}}
</el-tag>
</template>
</el-table-column>
</el-table>
</div>
<div class="row mt14">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
:page-sizes="[10, 20, 50, 100, 500]" background layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</div>
</div>
</template>
<script setup>
import dayjs from 'dayjs';
import { ref, reactive, onMounted } from 'vue'
import selectUser from '@/views/marketing_center/components/selectUser.vue';
import { multiplyAndFormat } from '@/utils'
import { distributionWithdrawFlow } from '@/api/coupon'
const pending = ref(0)
const finish = ref(0)
const statusList = ref([
{
value: 'pending',
label: '提现中',
type: 'primary'
},
{
value: 'success',
label: '可提现',
type: 'success'
},
{
value: 'finish',
label: '已完成',
type: 'info'
}
])
const queryForm = ref({
userId: '',
startTime: '',
endTime: ''
})
function searchHandle() {
tableData.page = 1;
getTableData()
}
function resetHandle() {
queryForm.value.userId = ''
queryForm.value.startTime = ''
queryForm.value.endTime = ''
times.value = []
searchHandle()
}
const tableData = reactive({
loading: false,
page: 1,
size: 10,
total: 0,
list: []
})
// 选择日期
const times = ref([])
function selectTimeChange(e) {
queryForm.value.startTime = dayjs(e[0]).format('YYYY-MM-DD 00:00:00')
queryForm.value.endTime = dayjs(e[1]).format('YYYY-MM-DD 23:59:59')
}
// 分页大小发生变化
function handleSizeChange(e) {
tableData.size = e;
getTableData();
}
// 分页发生变化
function handleCurrentChange(e) {
tableData.page = e;
getTableData();
}
async function getTableData() {
try {
tableData.loading = true
const res = await distributionWithdrawFlow({
...queryForm.value,
page: tableData.page,
size: tableData.size
})
tableData.list = res.records
tableData.total = res.totalRow
pending.value = res.pending
finish.value = res.finish
} catch (error) {
console.log(error);
}
setTimeout(() => {
tableData.loading = false
}, 500);
}
onMounted(() => {
getTableData()
})
</script>
<style scoped lang="scss">
.title {
padding: 10px 0 20px 0;
font-size: 16px;
font-weight: bold;
border-bottom: 1px solid #ececec;
}
.gyq_container {
padding: 14px;
.gyq_content {
padding: 14px;
background-color: #fff;
border-radius: 8px;
}
}
.row {
&.mt14 {
margin-top: 14px;
}
}
.info_wrap {
display: flex;
gap: 48px;
.item {
display: flex;
align-items: center;
border: 1px solid #D9D9D9;
border-radius: 8px;
padding: 0 10px;
.icon {
width: 44px;
height: 44px;
.img {
width: 100%;
height: 100%;
}
}
.info {
flex: 1;
padding: 10px;
}
}
}
.column {
display: flex;
flex-direction: column;
}
</style>