新增直营列表

This commit is contained in:
dzl
2025-09-09 14:42:37 +08:00
parent 2d7d81e1b6
commit 8b40dcceca
4 changed files with 472 additions and 0 deletions

View File

@@ -56,6 +56,11 @@
<el-icon><Money /></el-icon>
<template #title>转账管理</template>
</el-menu-item>
<el-menu-item index="/direct-sale">
<el-icon><Coin /></el-icon>
<template #title>直营列表</template>
</el-menu-item>
<el-menu-item v-if="userStore.isAdmin" index="/daily-transfer-stats">
<el-icon><DataAnalysis /></el-icon>

View File

@@ -48,6 +48,15 @@ const routes = [
icon: 'Money'
}
},
{
path: 'direct-sale',
name: 'DirectSale',
component: () => import('@/views/DirectSale.vue'),
meta: {
title: '直接销售 - 代理后台管理系统',
icon: 'Coin'
}
},
{
path: 'commissions',
name: 'Commissions',

View File

@@ -203,6 +203,14 @@ const api = {
getTransferStats: () => request.get('/agents/transfers/stats')
},
// 直营列表
directSale: {
getStats: () => request.get('/direct-sale/stats'),// 获取整体数据
getDirectSales: (params) => request.get('/direct-sale', {params}),// 获取直营列表
createDirectSale: (data) => request.post('/direct-sale', data),// 创建直营
withdraw: (id) => request.post(`/direct-sale/${id}/withdraw`),// 提现
},
// 文件上传
upload: {
uploadImage: (file) => {

450
src/views/DirectSale.vue Normal file
View File

@@ -0,0 +1,450 @@
<template>
<div class="direct-sale-container">
<div class="page-header">
<h1>直营列表</h1>
<el-button type="primary" @click="createDirectSaler">
<el-icon><Plus /></el-icon>
创建直营
</el-button>
</div>
<!-- 搜索和筛选 -->
<div class="search-section">
<el-card>
<el-form :model="searchForm" inline>
<el-form-item label="关键词">
<el-input
v-model="searchForm.search"
placeholder="请输入姓名或手机号"
clearable
style="width: 250px"
/>
</el-form-item>
<el-form-item label="等级">
<el-select
v-model="searchForm.level"
placeholder="请选择等级"
clearable
style="width: 150px"
>
<el-option label="全部" value=""/>
<el-option label="普通" value="normal"/>
<el-option label="VIP" value="vip"/>
<el-option label="SVIP" value="svip"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">
<el-icon>
<Search/>
</el-icon>
搜索
</el-button>
<el-button @click="handleReset">
<el-icon>
<Refresh/>
</el-icon>
重置
</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
<!-- 统计信息 -->
<div class="stats-section">
<el-row :gutter="20">
<el-col :span="6">
<el-card class="stats-card">
<div class="stats-content">
<div class="stats-icon">
<el-icon color="#409EFF">
<User/>
</el-icon>
</div>
<div class="stats-info">
<div class="stats-value">{{ directSaleStats.total_users || 0 }}</div>
<div class="stats-label">总用户数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stats-card">
<div class="stats-content">
<div class="stats-icon">
<el-icon color="#67C23A">
<Coin/>
</el-icon>
</div>
<div class="stats-info">
<div class="stats-value">{{ directSaleStats.total_beans || 0 }}</div>
<div class="stats-label">总融豆数量</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stats-card">
<div class="stats-content">
<div class="stats-icon">
<el-icon color="#E6A23C">
<Money/>
</el-icon>
</div>
<div class="stats-info">
<div class="stats-value">{{ directSaleStats.today_withdrawals || 0 }}</div>
<div class="stats-label">今日提现</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="stats-card">
<div class="stats-content">
<div class="stats-icon">
<el-icon color="#F56C6C">
<TrendCharts/>
</el-icon>
</div>
<div class="stats-info">
<div class="stats-value">{{ directSaleStats.total_withdrawals || 0 }}</div>
<div class="stats-label">总提现次数</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<!-- 直营列表表格 -->
<div class="table-section">
<el-card>
<el-table
v-loading="loading"
:data="directSaleList"
stripe
style="width: 100%"
>
<el-table-column prop="id" label="ID" width="80"/>
<el-table-column prop="name" label="姓名" width="120"/>
<el-table-column label="手机号" width="150">
<template #default="{ row }">
{{ maskPhoneNumber(row.phone) }}
</template>
</el-table-column>
<el-table-column prop="beans_count" label="融豆数量" width="120">
<template #default="{ row }">
<span class="beans-amount">{{ row.beans_count }}</span>
</template>
</el-table-column>
<el-table-column prop="level" label="等级" width="100">
<template #default="{ row }">
<el-tag :type="getLevelType(row.level)">
{{ getLevelText(row.level) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="created_at" label="注册时间" width="250">
<template #default="{ row }">
{{ formatDate(row.created_at) }}
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="{ row }">
<el-button
type="primary"
size="small"
@click="handleWithdraw(row)"
>
提现
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.size"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
</div>
</div>
</template>
<script setup>
import {ref, reactive, onMounted} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import {
Search,
Refresh,
User,
Coin,
Money,
TrendCharts
} from '@element-plus/icons-vue'
import api from '@/utils/api'
import {maskPhoneNumber} from '@/utils/public_method'
// 响应式数据
const loading = ref(false)
const directSaler = ref({})
const directSaleList = ref([])
const directSaleStats = ref({
total_users: 0,
total_beans: 0,
today_withdrawals: 0,
total_withdrawals: 0
})
// 搜索表单
const searchForm = reactive({
search: '',
min_beans: null,
max_beans: null,
level: ''
})
// 分页信息
const pagination = reactive({
page: 1,
size: 20,
total: 0
})
// 创建直营
const createDirectSaler = async () => {
try {
console.log("创建直营数据:",directSaler)
await api.directSale.createDirectSale(directSaler.value)
ElMessage.success('创建直营成功')
getDirectSaleList()
} catch (error) {
console.log(error)
ElMessage.error('创建直营失败')
}
}
// 获取直营列表
const getDirectSaleList = async () => {
loading.value = true
try {
const params = {
page: pagination.page,
size: pagination.size,
search: searchForm.search,
min_beans: searchForm.min_beans,
max_beans: searchForm.max_beans,
level: searchForm.level
}
const response = await api.directSale.getDirectSales(params)
directSaleList.value = response.data.data.direct_sales
pagination.total = response.data.data.pagination.total
} catch (error) {
directSaleList.value = [{
id: 1,
name: '张三',
phone: '13800000000',
beans_count: 1000,
level: 'normal',
created_at: '2023-01-01 00:00:00'
},{
id: 2,
name: '李四',
phone: '13900000000',
beans_count: 2000,
level: 'vip',
created_at: '2023-01-02 00:00:00'
}]
console.log(error)
ElMessage.error('获取直营列表失败')
} finally {
loading.value = false
}
}
// 获取统计数据
const getDirectSaleStats = async () => {
try {
const response = await api.directSale.getStats()
directSaleStats.value = response.data.data
} catch (error) {
console.log(error)
ElMessage.error('获取统计数据失败')
}
}
// 搜索
const handleSearch = () => {
pagination.page = 1
getDirectSaleList()
}
// 重置
const handleReset = () => {
searchForm.search = ''
searchForm.min_beans = null
searchForm.max_beans = null
searchForm.level = ''
pagination.page = 1
getDirectSaleList()
}
// 分页大小改变
const handleSizeChange = (size) => {
pagination.size = size
pagination.page = 1
getDirectSaleList()
}
// 当前页改变
const handleCurrentChange = (page) => {
pagination.page = page
getDirectSaleList()
}
// 处理提现
const handleWithdraw = async (row) => {
try {
await ElMessageBox.confirm(
`确定要为用户 ${row.name} 进行提现操作吗?`,
'提现确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
const response = await api.directSale.withdraw(row.id)
ElMessage.success('提现操作成功')
// 刷新列表和统计数据
getDirectSaleList()
getDirectSaleStats()
} catch (error) {
if (error !== 'cancel') {
console.log(error)
ElMessage.error('提现操作失败')
}
}
}
// 格式化日期
const formatDate = (date) => {
if (!date) return '-'
return new Date(date).toLocaleString('zh-CN')
}
// 获取等级类型
const getLevelType = (level) => {
const levelMap = {
'normal': '',
'vip': 'success',
'svip': 'warning'
}
return levelMap[level] || ''
}
// 获取等级文本
const getLevelText = (level) => {
const levelMap = {
'normal': '普通',
'vip': 'VIP',
'svip': 'SVIP'
}
return levelMap[level] || '普通'
}
// 组件挂载时获取数据
onMounted(() => {
getDirectSaleList()
getDirectSaleStats()
})
</script>
<style scoped>
.direct-sale-container {
padding: 20px;
}
/* .page-header {
margin-bottom: 20px;
} */
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-header h1 {
margin: 0;
font-size: 24px;
font-weight: 600;
color: #303133;
}
.search-section {
margin-bottom: 20px;
}
.stats-section {
margin-bottom: 20px;
}
.stats-card {
height: 100px;
}
.stats-content {
display: flex;
align-items: center;
height: 100%;
}
.stats-icon {
font-size: 32px;
margin-right: 16px;
}
.stats-info {
flex: 1;
}
.stats-value {
font-size: 24px;
font-weight: 600;
color: #303133;
margin-bottom: 4px;
}
.stats-label {
font-size: 14px;
color: #909399;
}
.table-section {
margin-bottom: 20px;
}
.beans-amount {
color: #67C23A;
font-weight: 600;
}
.pagination-container {
display: flex;
justify-content: center;
margin-top: 20px;
}
</style>