HaniBlindBox/server/php/app/admin/view/Statistics/profit.html
2026-01-01 20:46:07 +08:00

614 lines
31 KiB
HTML
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.

{include file="Public:header3"/}
<body>
<div class="layui-fluid" style="padding: 0px;">
<div class="layui-card">
<!-- 搜索条件区域 -->
<form class="layui-form layui-card-header layuiadmin-card-header-auto" style="padding-top: 15px;">
<div class="layui-form-item">
<div class="layui-inline">
<div class="layui-input-inline" style="width:150px;margin-left: 0px">
<input type="text" name="goodId" id="goodId" placeholder="请输入盒子Id" autocomplete="off"
class="layui-input">
</div>
</div>
<div class="layui-inline">
<div class="layui-input-inline" style="width:150px;margin-left: 0px">
<input type="text" name="title" id="title" placeholder="请输入盒子名称" autocomplete="off"
class="layui-input">
</div>
</div>
<div class="layui-inline">
<div class="layui-input-inline" style="width: 180px;margin-left: 0px">
<select name="status" id="status" style="width:100%">
<option value="">--请选择盒子状态--</option>
<option value="1">上架</option>
<option value="2">下架</option>
<option value="3">售罄</option>
</select>
</div>
</div>
<div class="layui-inline">
<div class="layui-input-inline" style="width: 180px;margin-left: 0px">
<select name="type" id="goodsType" style="width:100%">
<option value="">--盒子类型--</option>
</select>
</div>
</div>
<div class="layui-inline">
<div class="layui-input-inline" style="width:300px;margin-left: 0px">
<input type="text" id="addtime" name="addtime" class="layui-input" placeholder="选择时间"
autocomplete="off">
</div>
</div>
<div class="layui-inline">
<button class="layui-btn layuiadmin-btn-useradmin" id="searchBtn" type="button">
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
</button>
</div>
<div class="layui-inline">
<button class="layui-btn layui-btn-normal" id="exportBtn" type="button">
<i class="layui-icon layui-icon-export"></i> 导出Excel
</button>
</div>
</div>
</form>
<!-- 统计摘要区域 -->
<div class="layui-card-body">
<div class="layui-row layui-col-space15" id="statisticsSummary">
<div class="layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">
总收入
<i class="layui-icon layui-icon-about" style="font-size: 14px; color: #1E9FFF;"
title="收入 = 微信支付 + 钻石支付"></i>
</div>
<div class="layui-card-body" style="font-size: 24px; color: #01AAED;">
¥ <span id="totalIncome">0.00</span>
</div>
</div>
</div>
<div class="layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">
总出货价值
<i class="layui-icon layui-icon-about" style="font-size: 14px; color: #FFB800;"
title="出货价值 = 奖品的采购价格"></i>
</div>
<div class="layui-card-body" style="font-size: 24px; color: #FFB800;">
¥ <span id="totalCost">0.00</span>
</div>
</div>
</div>
<div class="layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">
总利润
<i class="layui-icon layui-icon-about" style="font-size: 14px; color: #5FB878;"
title="利润 = 收入 - (出货价值 - 兑换成本)"></i>
</div>
<div class="layui-card-body" style="font-size: 24px;">
¥ <span id="totalProfit" class="profit-value profit-detail">0.00</span>
</div>
</div>
</div>
<div class="layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">总兑换/发货价值</div>
<div class="layui-card-body" style="font-size: 24px;">
<span style="color: #FF9800;">¥ <span id="totalReMoney">0.00</span></span> /
<span style="color: #673AB7;">¥ <span id="totalFhMoney">0.00</span></span>
</div>
</div>
</div>
</div>
<!-- 数据表格区域 -->
<table id="profitTable" lay-filter="profitTable"></table>
<!-- 表格操作栏模板 -->
<script type="text/html" id="operationTpl">
<button class="layui-btn layui-btn-danger layui-btn-xs" lay-event="viewProductsOverview">出货概览</button>
<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="viewLotteryUsers">抽奖用户</button>
</script>
<!-- 利润和利润率模板 -->
<script type="text/html" id="profitTpl">
<span class="layui-badge {{d.profit >= 0 ? 'layui-bg-green' : 'layui-bg-red'}}">
¥ {{d.profit.toFixed(2)}}
</span>
</script>
<script type="text/html" id="profitRateTpl">
<span class="layui-badge {{d.profit_rate >= 0 ? 'layui-bg-green' : 'layui-bg-red'}}">
{{d.profit_rate.toFixed(2)}}%
</span>
</script>
</div>
</div>
</div>
{include file="Public:footer3"/}
<script type="text/javascript">
layui.use(['layer', 'laydate', 'table', 'form'], function () {
var $ = layui.$;
var layer = layui.layer;
var laydate = layui.laydate;
var form = layui.form;
var table = layui.table;
// 日期时间范围
laydate.render({
elem: '#addtime',
type: 'datetime',
range: true
});
// 加载盒子类型数据
function loadGoodsTypes() {
$.ajax({
url: '{:url("/admin/api/goods/types")}',
type: 'GET',
success: function (res) {
if (res.code === 0) {
// 更新下拉框
var html = '<option value="">--盒子类型--</option>';
$.each(res.data, function (index, item) {
html += '<option value="' + item.value + '" title="' + item.remark + '">' + item.fl_name + '</option>';
});
$('#goodsType').html(html);
form.render('select');
}
}
});
}
// 全局变量,存储汇总数据
var summaryData = {
totalIncome: 0,
totalCost: 0,
totalProfit: 0,
totalReMoney: 0,
totalFhMoney: 0
};
// 全局变量,用于控制异步加载
var loadingQueue = [];
var isLoading = false;
var maxConcurrentRequests = 3; // 最大并发请求数
// 初始化表格
var profitTable = table.render({
elem: '#profitTable',
url: '{:url("/admin/statistics/profitData")}',
method: 'get',
page: true,
height:'full-180',
limit: 50,
where: getSearchParams(),
parseData: function (res) {
if (res.code === 0) {
// 清空汇总数据
summaryData = {
totalIncome: 0,
totalCost: 0,
totalProfit: 0,
totalReMoney: 0,
totalFhMoney: 0
};
// 更新统计摘要数据为0
updateStatisticsSummary(summaryData);
return {
"code": 0,
"msg": res.msg,
"count": res.count,
"data": res.data
};
}
return {
"code": res.code,
"msg": res.msg,
"count": 0,
"data": []
};
},
cols: [[
{ field: 'id', title: '盒子ID', width: 80, sort: true, fixed: 'left' },
{ field: 'title', title: '盒子名称', width: 180 },
{
templet: function (d) {
var statusHtml = '';
if (d.status == 1) {
statusHtml = '<span class="layui-badge layui-bg-green">上架</span>';
} else if (d.status == 2) {
statusHtml = '<span class="layui-badge layui-bg-gray">下架</span>';
} else if (d.status == 3) {
statusHtml = '<span class="layui-badge layui-bg-orange">售罄</span>';
} else {
statusHtml = '<span class="layui-badge layui-bg-orange">自动下架</span>';
}
return '<div><button class="layui-btn layui-btn-normal layui-btn-radius layui-btn-xs type-btn" data-type="' + d.type + '">' + d.type_name + '</button> / ' + statusHtml + '</div>';
}, title: '盒子类型/状态', width: 160
},
{ field: 'price', title: '盒子单价', width: 100, templet: '<div>¥ {{d.price}}</div>' },
{
field: 'cj_count', title: '抽奖次数', width: 90, templet: function (d) {
if (!d.loaded) {
return '<div><i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i></div>';
}
return '<div>' + d.cj_count + '</div>';
}
},
{
field: 'use_money', title: '收入 <i class="layui-icon layui-icon-about" title="微信支付+钻石支付"></i>', width: 140, templet: function (d) {
if (!d.loaded) {
return '<div><i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i></div>';
}
return '<div><span class="layui-badge layui-bg-blue">¥ ' + d.use_money.toFixed(2) + '</span></div>';
}
},
{
field: 'sc_money', title: '出货价值 <i class="layui-icon layui-icon-about" title="奖品的采购价格"></i>', width: 140, templet: function (d) {
if (!d.loaded) {
return '<div><i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i></div>';
}
return '<div><span class="layui-badge layui-bg-orange">¥ ' + d.sc_money.toFixed(2) + '</span></div>';
}
},
{
field: 're_money', title: '已兑换达达卷', width: 120, templet: function (d) {
if (!d.loaded) {
return '<div><i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i></div>';
}
return '<div><span class="layui-badge" style="background-color: #FF9800;">¥ ' + d.re_money.toFixed(2) + '</span></div>';
}
},
{
field: 'fh_money', title: '已申请发货', width: 110, templet: function (d) {
if (!d.loaded) {
return '<div><i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i></div>';
}
return '<div><span class="layui-badge" style="background-color: #673AB7;">¥ ' + d.fh_money.toFixed(2) + '</span></div>';
}
},
{
field: 'single_box_profit', title: '单盒子利润 <i class="layui-icon layui-icon-about" title="盒子单价 × 抽数 - 盒子出货总额"></i>', width: 120, templet: function (d) {
if (!d.loaded) {
return '<div><i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i></div>';
}
// 计算公式盒子单价x抽数-盒子出货总额
var totalIncome = d.price * d.cj_count;
var singleBoxProfit = totalIncome - d.sc_money;
var colorClass = singleBoxProfit >= 0 ? 'layui-bg-green' : 'layui-bg-red';
return '<div><span class="layui-badge ' + colorClass + '">¥ ' + singleBoxProfit.toFixed(2) + '</span></div>';
}
},
{
field: 'single_box_profit_rate', title: '单盒子利润比 <i class="layui-icon layui-icon-about" title="(盒子单价 × 抽数 - 盒子出货总额) / (盒子单价 × 抽数) × 100%"></i>', width: 140, templet: function (d) {
if (!d.loaded) {
return '<div><i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i></div>';
}
// 计算公式:(盒子单价x抽数-盒子出货总额)/(盒子单价x抽数)
var totalIncome = d.price * d.cj_count;
var singleBoxProfitRate = totalIncome > 0 ? ((totalIncome - d.sc_money) / totalIncome * 100) : 0;
var colorClass = singleBoxProfitRate >= 0 ? 'layui-bg-green' : 'layui-bg-red';
return '<div><span class="layui-badge ' + colorClass + '">' + singleBoxProfitRate.toFixed(2) + '%</span></div>';
}
},
{
field: 'profit', title: '利润 <i class="layui-icon layui-icon-about" title="收入-(出货价值-兑换成本)"></i>', width: 110, templet: function (d) {
if (!d.loaded) {
return '<div><i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i></div>';
}
var colorClass = d.profit >= 0 ? 'layui-bg-green' : 'layui-bg-red';
// 构建详细计算公式展示
var detailCalc = d.use_money.toFixed(2) + ' - (' + d.sc_money.toFixed(2) + ' - ' + d.re_money.toFixed(2) + ') = ' + d.profit.toFixed(2);
return '<div><span class="layui-badge ' + colorClass + ' profit-detail" data-calculation="' + detailCalc + '">¥ ' + d.profit.toFixed(2) + '</span></div>';
}
},
{
field: 'profit_rate', title: '利润率', width: 100, templet: function (d) {
if (!d.loaded) {
return '<div><i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i></div>';
}
var colorClass = d.profit_rate >= 0 ? 'layui-bg-green' : 'layui-bg-red';
return '<div><span class="layui-badge ' + colorClass + '">' + d.profit_rate.toFixed(2) + '%</span></div>';
}
},
{ title: '操作', width: 180, toolbar: '#operationTpl', fixed: 'right' }
]],
done: function (res) {
// 清空加载队列
loadingQueue = [];
isLoading = false;
// 将所有数据项添加到加载队列中
var tableData = table.cache.profitTable || [];
for (var i = 0; i < tableData.length; i++) {
if (!tableData[i].loaded) {
loadingQueue.push(tableData[i]);
}
}
// 加载汇总数据
loadSummaryStatistics();
// 开始加载详细统计数据
processLoadingQueue();
}
});
// 加载汇总统计数据
function loadSummaryStatistics() {
var params = getSearchParams();
$.ajax({
url: '{:url("/admin/statistics_getSummaryStatistics")}',
type: 'GET',
data: params,
success: function (res) {
if (res.code === 0) {
// 更新汇总数据
summaryData = res.data;
// 更新统计摘要显示
updateStatisticsSummary(summaryData);
}
}
});
}
// 处理加载队列
function processLoadingQueue() {
if (isLoading || loadingQueue.length === 0) return;
isLoading = true;
var requests = 0;
var activeRequests = 0;
// 获取当前需要处理的项目
while (requests < maxConcurrentRequests && loadingQueue.length > 0) {
var item = loadingQueue.shift();
requests++;
activeRequests++;
loadBoxStatistics(item, function () {
activeRequests--;
if (activeRequests === 0) {
isLoading = false;
processLoadingQueue();
}
});
}
}
// 加载单个盒子的统计数据
function loadBoxStatistics(item, callback) {
var params = getSearchParams();
$.ajax({
url: '{:url("/admin/statistics_getBoxStatistics")}',
type: 'GET',
data: {
goods_id: item.id,
addtime: params.addtime
},
success: function (res) {
if (res.code === 0) {
var data = res.data;
// 更新表格中的数据
var tableData = table.cache.profitTable;
for (var i = 0; i < tableData.length; i++) {
if (tableData[i].id === item.id) {
// 更新数据
tableData[i].use_money = data.use_money;
tableData[i].sc_money = data.sc_money;
tableData[i].re_money = data.re_money;
tableData[i].fh_money = data.fh_money;
tableData[i].cj_count = data.cj_count;
tableData[i].profit = data.profit;
tableData[i].profit_rate = data.profit_rate;
tableData[i].is_negative = data.is_negative;
tableData[i].loaded = true;
// 直接更新单元格内容,不重新渲染表格
var tr = $('#profitTable').next().find('tr[data-index="' + i + '"]');
if (tr.length > 0) {
// 设置背景色
if (data.is_negative) {
tr.css('background-color', '#ffebee');
}
// 更新单元格内容
tr.find('td[data-field="use_money"] div').html('<span class="layui-badge layui-bg-blue">¥ ' + data.use_money.toFixed(2) + '</span>');
tr.find('td[data-field="sc_money"] div').html('<span class="layui-badge layui-bg-orange">¥ ' + data.sc_money.toFixed(2) + '</span>');
tr.find('td[data-field="re_money"] div').html('<span class="layui-badge" style="background-color: #FF9800;">¥ ' + data.re_money.toFixed(2) + '</span>');
tr.find('td[data-field="fh_money"] div').html('<span class="layui-badge" style="background-color: #673AB7;">¥ ' + data.fh_money.toFixed(2) + '</span>');
tr.find('td[data-field="cj_count"] div').html(data.cj_count);
// 计算并更新单盒子利润
var totalIncome = tableData[i].price * data.cj_count;
var singleBoxProfit = totalIncome - data.sc_money;
var singleBoxProfitClass = singleBoxProfit >= 0 ? 'layui-bg-green' : 'layui-bg-red';
tr.find('td[data-field="single_box_profit"] div').html('<span class="layui-badge ' + singleBoxProfitClass + '">¥ ' + singleBoxProfit.toFixed(2) + '</span>');
// 计算并更新单盒子利润比
var singleBoxProfitRate = totalIncome > 0 ? ((totalIncome - data.sc_money) / totalIncome * 100) : 0;
var singleBoxColorClass = singleBoxProfitRate >= 0 ? 'layui-bg-green' : 'layui-bg-red';
tr.find('td[data-field="single_box_profit_rate"] div').html('<span class="layui-badge ' + singleBoxColorClass + '">' + singleBoxProfitRate.toFixed(2) + '%</span>');
var profitColorClass = data.profit >= 0 ? 'layui-bg-green' : 'layui-bg-red';
tr.find('td[data-field="profit"] div').html('<span class="layui-badge ' + profitColorClass + ' profit-detail" data-calculation="' + data.use_money.toFixed(2) + ' - (' + data.sc_money.toFixed(2) + ' - ' + data.re_money.toFixed(2) + ') = ' + data.profit.toFixed(2) + '">¥ ' + data.profit.toFixed(2) + '</span>');
var rateColorClass = data.profit_rate >= 0 ? 'layui-bg-green' : 'layui-bg-red';
tr.find('td[data-field="profit_rate"] div').html('<span class="layui-badge ' + rateColorClass + '">' + data.profit_rate.toFixed(2) + '%</span>');
}
break;
}
}
}
if (callback) callback();
},
error: function () {
if (callback) callback();
}
});
}
// 更新统计摘要
function updateStatisticsSummary(summary) {
if (!summary) return;
$('#totalIncome').text(summary.totalIncome.toFixed(2));
$('#totalCost').text(summary.totalCost.toFixed(2));
$('#totalProfit').text(summary.totalProfit.toFixed(2));
$('#totalReMoney').text(summary.totalReMoney.toFixed(2));
$('#totalFhMoney').text(summary.totalFhMoney.toFixed(2));
// 设置总利润的计算详情
var calcDetail = summary.totalIncome.toFixed(2) + ' - (' + summary.totalCost.toFixed(2) + ' - ' + summary.totalReMoney.toFixed(2) + ') = ' + summary.totalProfit.toFixed(2);
$('#totalProfit').attr('data-calculation', calcDetail);
// 设置利润颜色
var profitElem = $('#totalProfit');
if (summary.totalProfit >= 0) {
profitElem.parent().css('color', '#5FB878');
} else {
profitElem.parent().css('color', '#FF5722');
}
}
// 获取搜索参数
function getSearchParams() {
return {
goodId: $('#goodId').val(),
title: $('#title').val(),
status: $('#status').val(),
type: $('#goodsType').val(),
addtime: $('#addtime').val()
};
}
// 搜索按钮点击事件
$('#searchBtn').on('click', function () {
// 重新加载表格数据
profitTable.reload({
where: getSearchParams(),
page: {
curr: 1
}
});
// 重新加载汇总数据
loadSummaryStatistics();
});
// 导出按钮点击事件
$('#exportBtn').on('click', function () {
// 获取当前查询参数
var params = getSearchParams();
// 弹出确认对话框
layer.confirm('确定要导出当前条件下的所有盒子利润统计数据吗?', {
btn: ['确定', '取消'],
title: '导出确认'
}, function(index) {
layer.close(index);
// 显示加载层
var loadIndex = layer.load(2, {shade: [0.3, '#fff']});
// 构建导出URL及参数
var exportUrl = '{:url("/admin/statistics_exportProfit")}';
var queryString = $.param(params);
if (queryString) {
exportUrl += '?' + queryString;
}
// 创建一个隐藏的a标签用于下载
var downloadLink = document.createElement('a');
downloadLink.style.display = 'none';
downloadLink.href = exportUrl;
downloadLink.target = '_blank';
document.body.appendChild(downloadLink);
// 模拟点击下载
downloadLink.click();
// 移除临时元素
setTimeout(function() {
document.body.removeChild(downloadLink);
layer.close(loadIndex);
}, 2000);
});
});
// 监听表格工具条事件
table.on('tool(profitTable)', function (obj) {
var data = obj.data;
if (obj.event === 'viewProductsOverview') {
// 查看出货概览
layer.open({
type: 2,
title: '出货概览',
shadeClose: false,
shade: 0.3,
area: ['90%', '90%'],
content: '{:url("/admin/statistics_productsOverview")}?goods_id=' + data.id
});
} else if (obj.event === 'viewLotteryUsers') {
// 查看抽奖用户
layer.open({
type: 2,
title: data.title + ' - 抽奖用户列表',
shadeClose: false,
shade: 0.3,
area: ['90%', '90%'],
content: '{:url("/admin/statistics_lotteryUsers")}?goods_id=' + data.id
});
}
});
// 页面加载完成后执行
$(function () {
loadGoodsTypes();
// 初始化提示图标
initTips();
});
// 初始化提示信息
function initTips() {
// 为提示图标添加鼠标悬停事件
$('.layui-icon-about').on('mouseenter', function () {
var that = this;
layer.tips($(that).attr('title'), that, {
tips: [1, '#3595CC'],
time: 4000
});
});
// 为利润详情添加鼠标悬停事件(使用事件委托,处理动态加载的元素)
$(document).on('mouseenter', '.profit-detail', function () {
var that = this;
var calculation = $(that).data('calculation');
if (calculation) {
layer.tips(calculation, that, {
tips: [1, '#FF5722'],
time: 4000
});
}
});
}
});
</script>
</body>
</html>