面板
This commit is contained in:
parent
685e847208
commit
83dba34c3e
|
|
@ -13,6 +13,7 @@ use think\Collection;
|
||||||
use think\Exception;
|
use think\Exception;
|
||||||
use think\exception\DbException;
|
use think\exception\DbException;
|
||||||
use think\Loader;
|
use think\Loader;
|
||||||
|
use think\Model;
|
||||||
use think\response\Json;
|
use think\response\Json;
|
||||||
use function Symfony\Component\Clock\now;
|
use function Symfony\Component\Clock\now;
|
||||||
|
|
||||||
|
|
@ -25,7 +26,6 @@ class Item extends Backend
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected $relationSearch = true;
|
protected $relationSearch = true;
|
||||||
|
|
||||||
public function _initialize()
|
public function _initialize()
|
||||||
|
|
@ -38,14 +38,15 @@ class Item extends Backend
|
||||||
return $this->fetch('index');
|
return $this->fetch('index');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function list(){
|
public function list()
|
||||||
|
{
|
||||||
$build = new Order();
|
$build = new Order();
|
||||||
|
|
||||||
$start = now()->modify('-14 days')->format('Y-m-d');
|
$start = now()->modify('-14 days')->format('Y-m-d');
|
||||||
$end_at = now()->format('Y-m-d 23:29:59');
|
$end_at = now()->format('Y-m-d 23:29:59');
|
||||||
|
|
||||||
$filter ['daterange'] = request()->get('daterange');
|
$filter = json_decode(request()->get('filter','[]'),true);
|
||||||
|
|
||||||
if (!empty($filter['daterange'])) {
|
if (!empty($filter['daterange'])) {
|
||||||
$arr = explode(' - ', $filter['daterange']);
|
$arr = explode(' - ', $filter['daterange']);
|
||||||
if (trim($arr[0])) {
|
if (trim($arr[0])) {
|
||||||
|
|
@ -55,18 +56,134 @@ class Item extends Backend
|
||||||
$end_at = trim($arr[1]) . ' 23:29:59';
|
$end_at = trim($arr[1]) . ' 23:29:59';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$build->field([
|
$offset = request()->get('offset',0);
|
||||||
'item_title',
|
$limit = request()->get('limit',15);
|
||||||
'sum(total) total',
|
$build->whereBetween('create_time', [$start, $end_at])
|
||||||
'count(id) count',
|
->field([
|
||||||
'sum(performance) performance'
|
'item_title name', // 类型
|
||||||
])->group('item_title');
|
'sum(total) total', // 营业额
|
||||||
|
'count(id) count_num', // 单量
|
||||||
|
'count(if(status=60,1,null)) finish_num', // 单量
|
||||||
|
'sum(performance) performance', // 收益
|
||||||
|
'sum(cost) cost', // 收益
|
||||||
|
'sum(material_cost) material_cost', // 收益
|
||||||
|
'sum(refund_amount) refund_amount', // 公司退款
|
||||||
|
'sum(worker_refund_amount) worker_refund_amount', // 工人退款
|
||||||
|
])->group('item_title')
|
||||||
|
->order('count_num', 'desc');
|
||||||
|
// dd($total);
|
||||||
|
$res = $build->paginate();
|
||||||
|
$total = $res->total();
|
||||||
|
$ress = $res->items();
|
||||||
|
$data = [];
|
||||||
|
// dd($res);
|
||||||
|
foreach ($ress as $re) {
|
||||||
|
$re = $re->toArray();
|
||||||
|
// dd($re);
|
||||||
|
$cost_total = $re['cost'] + $re['material_cost'];
|
||||||
|
$name = explode('/', $re['name']);
|
||||||
|
$re['name'] = str_replace(' ', '', array_pop($name));
|
||||||
|
$re['performance_rate'] = $this->mydiv($re['performance'],$re['total']);
|
||||||
|
$re['trans_rate'] = $this->mydiv($re['finish_num'],$re['count_num']);
|
||||||
|
$re['cash_value'] = $this->mydiv($re['performance'],$re['count_num']);
|
||||||
|
$re['total_avg'] = $this->mydiv($re['total'],$re['count_num'],2,false);
|
||||||
|
$re['performance_avg'] = $this->mydiv($re['performance'],$re['finish_num'],2,false);
|
||||||
|
|
||||||
|
$re['cost_total'] = $cost_total;
|
||||||
|
// $re['id'] = $re['item_id'];
|
||||||
|
$data [] = $re;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'rows' => $data,
|
||||||
|
'total' => $total
|
||||||
|
];
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function chartData(){
|
public function chartData()
|
||||||
return [];
|
{
|
||||||
|
$build = new Order();
|
||||||
|
|
||||||
|
$start = now()->modify('-14 days')->format('Y-m-d');
|
||||||
|
$end_at = now()->format('Y-m-d 23:29:59');
|
||||||
|
|
||||||
|
$filter ['daterange'] = request()->post('daterange');
|
||||||
|
|
||||||
|
if (!empty($filter['daterange'])) {
|
||||||
|
$arr = explode(' - ', $filter['daterange']);
|
||||||
|
if (trim($arr[0])) {
|
||||||
|
$start = trim($arr[0]);
|
||||||
|
}
|
||||||
|
if (trim($arr[1])) {
|
||||||
|
$end_at = trim($arr[1]) . ' 23:29:59';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $build->whereBetween('create_time', [$start, $end_at])
|
||||||
|
->where('status', Order::STATUS_FINISHED)
|
||||||
|
->field([
|
||||||
|
'item_title name', // 类型
|
||||||
|
'sum(total) total', // 营业额
|
||||||
|
'count(id) count', // 单量
|
||||||
|
'sum(performance) performance', // 收益
|
||||||
|
'sum(refund_amount) refund_amount', // 公司退款
|
||||||
|
'sum(worker_refund_amount) worker_refund_amount', // 工人退款
|
||||||
|
])->group('item_title')
|
||||||
|
->order('count', 'desc')->select();
|
||||||
|
$data = [];
|
||||||
|
foreach ($res as $re) {
|
||||||
|
$re = $re->getData();
|
||||||
|
$name = explode('/', $re['name']);
|
||||||
|
$re['name'] = str_replace(' ', '', array_pop($name));
|
||||||
|
$data [] = $re;
|
||||||
|
}
|
||||||
|
|
||||||
|
$xAxis = [];
|
||||||
|
$totalPerformance = [];
|
||||||
|
$conversionRate = []; // 假设转化率 = performance / total(或自定义逻辑)
|
||||||
|
$profitRate = []; // 假设利润率 = (performance - worker_refund_amount) / performance
|
||||||
|
$refundRate = []; // 假设退款率 = refund_amount / total
|
||||||
|
$monetizedValue = []; // 假设变现值 = performance - refund_amount
|
||||||
|
|
||||||
|
foreach ($data as $item) {
|
||||||
|
$name = $item['name'];
|
||||||
|
$total = (float)$item['total'];
|
||||||
|
$performance = (float)$item['performance'];
|
||||||
|
$refund = (float)($item['refund_amount'] + $item['worker_refund_amount']);
|
||||||
|
$workerRefund = (float)$item['worker_refund_amount'];
|
||||||
|
$count = max((int)$item['count'], 1); // 避免除以0
|
||||||
|
|
||||||
|
$xAxis[] = $name;
|
||||||
|
$totalPerformance[] = $total;
|
||||||
|
$conversionRate[] = $total > 0 ? round($performance / $total * 100, 2) : 0;
|
||||||
|
$profitRate[] = $total > 0 ? round(($total - $workerRefund) / $total * 100, 2) : 0;
|
||||||
|
$refundRate[] = $total > 0 ? round($refund / $total * 100, 2) : 0;
|
||||||
|
$monetizedValue[] = $total / $count;
|
||||||
|
}
|
||||||
|
$result = [
|
||||||
|
'xAxis' => $xAxis,
|
||||||
|
'series' => [
|
||||||
|
['name' => '总业绩(¥)', 'type' => 'bar', 'yAxisIndex' => 0, 'data' => $totalPerformance],
|
||||||
|
['name' => '转化率(%)', 'type' => 'bar', 'yAxisIndex' => 1, 'data' => $conversionRate],
|
||||||
|
['name' => '利润率(%)', 'type' => 'bar', 'yAxisIndex' => 1, 'data' => $profitRate],
|
||||||
|
['name' => '退款率(%)', 'type' => 'bar', 'yAxisIndex' => 1, 'data' => $refundRate],
|
||||||
|
['name' => '变现值(¥)', 'type' => 'line', 'yAxisIndex' => 0, 'data' => $monetizedValue],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mydiv($a, $b, int $scale = 4, $is_percent = true): int|string
|
||||||
|
{
|
||||||
|
$val = $b > 0 ? bcdiv($a, $b, $scale) : 0;
|
||||||
|
|
||||||
|
if ($is_percent) {
|
||||||
|
|
||||||
|
return bcmul($val, 100, 2);
|
||||||
|
}
|
||||||
|
return $val;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,19 +24,9 @@
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<div class="form-inline" id="chart-filter" style="margin-top:20px;margin-bottom: 30px;">
|
<div class="form-inline" id="chart-filter" style="margin-top:20px;margin-bottom: 30px;">
|
||||||
<!-- 单选:维度 -->
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="radio-inline">
|
|
||||||
<input type="radio" name="filter[time_by]" value="1" checked> 派单时间
|
|
||||||
</label>
|
|
||||||
<label class="radio-inline">
|
|
||||||
<input type="radio" name="filter[time_by]" value="2"> 录单时间
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 时间范围 -->
|
<!-- 时间范围 -->
|
||||||
<div class="form-group" style="margin-left: 15px;">
|
<div class="form-group" style="margin-left: 15px;">
|
||||||
<input type="text" class="form-control datetimerange" data-locale='{"format":"YYYY-MM-DD"}' placeholder="指定日期" name="filter[daterange]" id="daterange" autocomplete="off" style="width: 170px;">
|
<input type="text" class="form-control datetimerange" data-locale='{"format":"YYYY-MM-DD"}' placeholder="指定日期" name="filter[daterange]" id="daterange" autocomplete="off" style="width: 200px;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 查询按钮 -->
|
<!-- 查询按钮 -->
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-theme', 'template','addtabs','moment'], function ($, undefined, Backend, Table, Form,Echarts,undefined,Template,Datatable,Moment) {
|
define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-theme', 'template','addtabs','moment'], function ($, undefined, Backend, Table, Form,echarts,undefined,Template,Datatable,Moment) {
|
||||||
|
|
||||||
var Controller = {
|
var Controller = {
|
||||||
|
|
||||||
|
|
@ -28,29 +28,28 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
|
||||||
searchFormVisible:true,
|
searchFormVisible:true,
|
||||||
columns: [
|
columns: [
|
||||||
[
|
[
|
||||||
//{field: 'id', title: __('Id')},
|
// {field: 'id', title: __('ID'),visible:true,operate: false},
|
||||||
{field: 'id', title: __('ID'),visible:true,operate: false},
|
{field: 'name', title: '项目类型',operate: false},
|
||||||
{field: 'admin_user', title: __('派单员'),operate: "LIKE"},
|
|
||||||
{field: 'count_num', title: __('总订单数'),operate: false},
|
|
||||||
{field: 'finish_num', title: __('完单数'),operate: false},
|
|
||||||
{field: 'total', title: __('成效额(¥)'), operate: false},
|
{field: 'total', title: __('成效额(¥)'), operate: false},
|
||||||
{field: 'performance', title: __('总业绩(¥)'), operate: false},
|
{field: 'performance', title: __('总业绩(¥)'), operate: false},
|
||||||
{field: 'cost_total', title: __('总成本(¥)'), operate: false},
|
{field: 'count_num', title: __('总订单数'),operate: false},
|
||||||
{field: 'refund_total', title: __('退款金额(¥)'), operate: false},
|
|
||||||
{field: 'refund_count', title: __('退款单数'), operate: false},
|
|
||||||
|
|
||||||
{field: 'performance_rate', title: __('利润率(%)'), operate: false},
|
{field: 'performance_rate', title: __('利润率(%)'), operate: false},
|
||||||
|
|
||||||
{field: 'trans_rate', title: __('转化率(%)'), operate: false},
|
{field: 'trans_rate', title: __('转化率(%)'), operate: false},
|
||||||
{field: 'cash_value', title: __('变现值'), operate: false},
|
{field: 'cash_value', title: __('变现值'), operate: false},
|
||||||
|
|
||||||
{field: 'performance_avg', title: __('客单利润(¥)'), operate: false},
|
{field: 'performance_avg', title: __('客单利润(¥)'), operate: false},
|
||||||
{field: 'total_avg', title: __('客单价(¥)'), operate: false},
|
{field: 'total_avg', title: __('客单价(¥)'), operate: false},
|
||||||
|
|
||||||
{field: 'avg_time_diff', title: __('派单时效(小时)'), operate: false},
|
{field: 'avg_time_diff', title: __('派单时效(小时)'), operate: false},
|
||||||
|
|
||||||
//{field: 'admin_user', title: __('派单员'),operate: "LIKE",visible:false},
|
{field: 'cost_total', title: __('总成本(¥)'), operate: false},
|
||||||
//{field: 'city_name', title: __('城市'),operate: "LIKE",visible:false},
|
{field: 'finish_num', title: __('完单数'),operate: false},
|
||||||
//{field: 'city_name', title: __('城市'),operate: "LIKE",visible:false},
|
|
||||||
{field: 'time_by', title: __('时间维度'), visible:false,searchList: {"1":__('录单时间'),"2":__('派单时间')},defaultValue:1, formatter: Table.api.formatter.normal},
|
{field: 'refund_count', title: __('退款单数'), operate: false},
|
||||||
|
{field: 'refund_total', title: __('退款金额(¥)'), operate: false},
|
||||||
|
|
||||||
{field: 'daterange', title: __('时间筛选'), addclass:'datetimerange',
|
{field: 'daterange', title: __('时间筛选'), addclass:'datetimerange',
|
||||||
autocomplete:false,
|
autocomplete:false,
|
||||||
operate: "RANGE",
|
operate: "RANGE",
|
||||||
|
|
@ -60,24 +59,26 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
|
||||||
visible:false,
|
visible:false,
|
||||||
defaultValue: Config.default_daterange
|
defaultValue: Config.default_daterange
|
||||||
},
|
},
|
||||||
{field: 'operate', title: __('Operate'), table: table2, events: Table.api.events.operate, formatter: Table.api.formatter.operate,
|
// {field: 'operate', title: __('Operate'), table: table2, events:
|
||||||
buttons: [
|
// Table.api.events.operate,
|
||||||
{
|
// formatter: Table.api.formatter.operate,
|
||||||
name: 'aftersales',
|
// buttons: [
|
||||||
text:"售后列表",
|
// {
|
||||||
title:"售后列表",
|
// name: 'aftersales',
|
||||||
icon: 'fa fa-list',
|
// text:"售后列表",
|
||||||
url: function(row){
|
// title:"售后列表",
|
||||||
return 'aftersales/aftersale/index?from=2&man_id='+row.id;
|
// icon: 'fa fa-list',
|
||||||
},
|
// url: function(row){
|
||||||
extend: 'data-toggle="tooltip" data-container="body"',
|
// return 'aftersales/aftersale/index?from=2&man_id='+row.id;
|
||||||
classname: 'btn btn-xs btn-default btn-dialog',
|
// },
|
||||||
visible:function(row){
|
// extend: 'data-toggle="tooltip" data-container="body"',
|
||||||
return true;
|
// classname: 'btn btn-xs btn-default btn-dialog',
|
||||||
}
|
// visible:function(row){
|
||||||
},
|
// return true;
|
||||||
]
|
// }
|
||||||
}
|
// },
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
@ -87,8 +88,6 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 触发 tab 后发起 ajax 获取图表数据
|
// 触发 tab 后发起 ajax 获取图表数据
|
||||||
$('ul.nav-tabs li.active a[data-toggle="tab"]').on("shown.bs.tab", function () {
|
$('ul.nav-tabs li.active a[data-toggle="tab"]').on("shown.bs.tab", function () {
|
||||||
getChartData();
|
getChartData();
|
||||||
|
|
@ -96,16 +95,12 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
|
||||||
|
|
||||||
|
|
||||||
function getChartData(){
|
function getChartData(){
|
||||||
// 获取单选框选中的值
|
|
||||||
var timeBy = $('input[name="filter[time_by]"]:checked').val();
|
|
||||||
|
|
||||||
// 获取日期范围值
|
// 获取日期范围值
|
||||||
var daterange = $('#daterange').val();
|
var daterange = $('#daterange').val();
|
||||||
|
|
||||||
// 构建查询参数
|
// 构建查询参数
|
||||||
var params = {
|
var params = {
|
||||||
'time_by': timeBy,
|
'daterange': daterange,
|
||||||
'daterange': daterange
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
|
@ -114,7 +109,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
data:params,
|
data:params,
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
|
Controller.api.chart(response);
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
console.error("图表数据加载失败");
|
console.error("图表数据加载失败");
|
||||||
|
|
@ -187,6 +182,35 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
|
||||||
api: {
|
api: {
|
||||||
bindevent: function () {
|
bindevent: function () {
|
||||||
Form.api.bindevent($("form[role=form]"));
|
Form.api.bindevent($("form[role=form]"));
|
||||||
|
},
|
||||||
|
chart(chartData){
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: { type: 'shadow' }
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: chartData.series.map(s => s.name)
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: chartData.xAxis
|
||||||
|
},
|
||||||
|
yAxis: [
|
||||||
|
{ type: 'value', name: '金额 (¥)' },
|
||||||
|
{ type: 'value', name: '比率 (%)' }
|
||||||
|
],
|
||||||
|
series: chartData.series
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化图表
|
||||||
|
const myChart = echarts.init(document.getElementById('bar-chart'));
|
||||||
|
myChart.setOption(option);
|
||||||
|
|
||||||
|
// 监听窗口大小变化,自动重新绘制图表
|
||||||
|
window.addEventListener('resize', function() {
|
||||||
|
myChart.resize();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user