Accept Merge Request #39: (feature/hant -> develop)

Merge Request: 面板

Created By: @todayswind
Accepted By: @todayswind
URL: https://g-bcrc3009.coding.net/p/allocatr/d/allocatr/git/merge/39?initial=true
This commit is contained in:
todayswind 2025-05-06 23:14:25 +08:00 committed by Coding
commit 476e61e71d
3 changed files with 307 additions and 69 deletions

View File

@ -3,8 +3,10 @@
namespace app\admin\controller; namespace app\admin\controller;
use app\admin\addresmart\Address; use app\admin\addresmart\Address;
use app\admin\model\Abnormal;
use app\admin\model\AuthGroupAccess; use app\admin\model\AuthGroupAccess;
use app\admin\model\order\Invoice; use app\admin\model\order\Invoice;
use app\admin\model\OrderAbnormal;
use app\admin\model\OrderDispatch; use app\admin\model\OrderDispatch;
use app\admin\model\Worker; use app\admin\model\Worker;
use app\admin\model\WorkerItem; use app\admin\model\WorkerItem;
@ -41,16 +43,246 @@ class Orderplan extends Backend
parent::_initialize(); parent::_initialize();
$this->model = new \app\admin\model\Order; $this->model = new \app\admin\model\Order;
//$this->view->assign("collectList", $this->model->getCollectList());
//$this->view->assign("dispatchTypeList", $this->model->getDispatchTypeList());
} }
public function dashboard(){ public function dashboard()
{
return $this->fetch('orderplan/index'); return $this->fetch('orderplan/index');
} }
public function data()
{
$top = $this->getTopTotal();
$lines = $this->getLine();
$pie = $this->getPie();
$this->success(data: [
'top' => $top,
'lines' => $lines,
'pie' => $pie,
]);
}
private function getTopTotal()
{
$build = new \app\admin\model\Order();
$build->field([
'sum(total) total',
'count(id) count',
'sum(performance) performance'
])->limit(1);
$res = $this->buildDate($build)->select();
$res = $res[0] ?? false;
$worker = Worker::where('status', 1)->count();
if ($res) {
return [
'total' => $res->total,
'count' => $res->count,
'performance' => $res->performance,
'worker' => $worker,
];
}
return [
'total' => 0,
'count' => 0,
'performance' => 0,
'worker' => 0,
];
}
private function getLine()
{
$build = new \app\admin\model\Order();
$start = now()->modify('-14 days')->format('Y-m-d');
$end_at = now()->format('Y-m-d');
$build->field([
'DATE(create_time) day',
'sum(total) total',
'count(id) count',
'sum(performance) performance'
])->group(' DATE(create_time)');
$res = $this->buildDate($build)->select();
$data = [];
foreach ($res as $re) {
$re = $re->getData();
$data [] = $re;
}
return $this->prepareEchartsBarData($data, $start, $end_at);
}
private function getPie()
{
$build = new OrderAbnormal();
$res = $this->buildDate($build)
->field([
'abnormal_title name',
'count(id) value',
])
->group('abnormal_title')
->select();
$data = [];
foreach ($res as $re) {
$re = $re->getData();
$data [] = $re;
}
$out ['cancel'] = $data;
$build = new \app\admin\model\Order();
$res = $this->buildDate($build)
->field([
'item_title name',
'count(id) value',
])
->group('item_title')
->select();
$data = [];
foreach ($res as $re) {
$re = $re->getData();
$name = explode('/',$re['name']);
$re['name'] = str_replace(' ', '' ,array_pop($name));
$data [] = $re;
}
$out ['item'] = $data;
$build = new \app\admin\model\Order();
$res = $this->buildDate($build)
->field([
'SUBSTRING(area_id, 1, 2) AS name',
'count(id) value',
])
->group('name')
->select();
$data = [];
foreach ($res as $re) {
$re = $re->getData();
$re['name'] = $this->getProvinceByCode($re['name']);
$data [] = $re;
}
$out ['area'] = $data;
$build = new \app\admin\model\Order();
$res = $this->buildDate($build)
->field([
'source_shop AS name',
'count(id) value',
])
->group('name')
->select();
$data = [];
foreach ($res as $re) {
$re = $re->getData();
$data [] = $re;
}
$out ['source'] = $data;
return $out;
}
private function buildDate($build)
{
$start = now()->modify('-14 days')->format('Y-m-d');
$end_at = now()->format('Y-m-d');
$build->where('create_time', 'between', [$start, $end_at]);
return $build;
}
private function prepareEchartsBarData(array $data, string $startDate, string $endDate): array
{
// 将原始数据用日期作为键索引
$indexed = [];
foreach ($data as $item) {
$indexed[$item['day']] = $item;
}
$start = strtotime($startDate);
$end = strtotime($endDate);
$xAxis = [];
$series_total = [];
$series_count = [];
$series_performance = [];
$rate = [];
for ($date = $start; $date <= $end; $date += 86400) {
$day = date('Y-m-d', $date);
$xAxis[] = $day;
$total = isset($indexed[$day]) ? (float)$indexed[$day]['total'] : 0;
$count = isset($indexed[$day]) ? (int)$indexed[$day]['count'] : 0;
$performance = isset($indexed[$day]) ? (float)$indexed[$day]['performance'] : 0;
$series_total[] = $total;
$series_count[] = $count;
$series_performance[] = $performance;
$rate[] = $total ? number_format($performance / $total * 100, 2) : '0';
}
return [
'xAxis' => $xAxis,
'series' => [
'total' => $series_total,
'count' => $series_count,
'performance' => $series_performance,
'rate' => $rate,
],
];
}
function getProvinceByCode($code) {
// 省级行政区域编码对应数组
$provinces = [
11 => '北京',
12 => '天津',
13 => '河北',
14 => '山西',
15 => '内蒙古',
21 => '辽宁',
22 => '吉林',
23 => '黑龙江',
31 => '上海',
32 => '江苏',
33 => '浙江',
34 => '安徽',
35 => '福建',
36 => '江西',
37 => '山东',
41 => '河南',
42 => '湖北',
43 => '湖南',
44 => '广东',
45 => '广西',
46 => '海南',
50 => '重庆',
51 => '四川',
52 => '贵州',
53 => '云南',
54 => '西藏',
61 => '陕西',
62 => '甘肃',
63 => '青海',
64 => '宁夏',
65 => '新疆',
71 => '台湾',
81 => '香港',
82 => '澳门'
];
// 如果提供的代码存在于数组中,返回对应的省名称,否则返回未知
return isset($provinces[$code]) ? $provinces[$code] : '未知';
}
} }

View File

@ -26,7 +26,7 @@
<span>订单量</span> <span>订单量</span>
</div> </div>
<div class="myplan-num"> <div class="myplan-num">
1,240 loading
</div> </div>
</div> </div>
</div> </div>
@ -39,7 +39,7 @@
<span>总业绩</span> <span>总业绩</span>
</div> </div>
<div class="myplan-num"> <div class="myplan-num">
1,240 loading
</div> </div>
</div> </div>
</div> </div>
@ -52,7 +52,7 @@
<span>师傅总数</span> <span>师傅总数</span>
</div> </div>
<div class="myplan-num"> <div class="myplan-num">
1,240 loading
</div> </div>
</div> </div>
</div> </div>

View File

@ -2,13 +2,20 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
var Controller = { var Controller = {
dashboard: function () { dashboard: function () {
Fast.api.ajax({
url: 'orderplan/data',
}, function (data, ret) {
Controller.echarts.top(data.top);
Controller.echarts.money_line(data.lines);
Controller.echarts.cancel_order_pie(data.pie.cancel);
Controller.echarts.item_pie(data.pie.item);
Controller.echarts.area_pie(data.pie.area);
Controller.echarts.source_pie(data.pie.source);
});
Controller.api.bindevent(); Controller.api.bindevent();
Controller.echarts.item_pie();
Controller.echarts.area_pie();
Controller.echarts.source_pie();
Controller.echarts.cancel_order_pie(); Controller.echarts.cancel_order_pie();
Controller.echarts.money_line();
console.log(123); console.log(123);
}, },
@ -24,13 +31,26 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
} }
}, },
echarts: { echarts: {
item_pie: function(){ top: function(data){
let map = [
'count','performance','worker'
];
$('.myplan-num').each(function(index) {
if (index === 1){
$(this).html('¥' + data[map[index]]);
}else {
$(this).html(data[map[index]]);
}
});
},
item_pie: function(data){
// 基本的饼图配置 // 基本的饼图配置
var myChart = echarts.init(document.getElementById('item_pie')); var myChart = echarts.init(document.getElementById('item_pie'));
var option = { var option = {
title: { title: {
text: '取消订单类型', text: '订单项目类型',
subtext: '', subtext: '',
left: 'right' left: 'right'
}, },
@ -49,7 +69,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
legend: { legend: {
orient: 'vertical', orient: 'vertical',
left: 'left', left: 'left',
data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎'] data: data.map(item => item.name)
}, },
series: [ series: [
{ {
@ -57,13 +77,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
type: 'pie', type: 'pie',
radius: '50%', radius: '50%',
center: ['50%', '60%'], center: ['50%', '60%'],
data: [ data: data,
{ value: 335, name: '直接访问' },
{ value: 310, name: '邮件营销' },
{ value: 234, name: '联盟广告' },
{ value: 135, name: '视频广告' },
{ value: 1548, name: '搜索引擎' }
],
emphasis: { emphasis: {
itemStyle: { itemStyle: {
shadowBlur: 10, shadowBlur: 10,
@ -82,13 +96,13 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
myChart.resize(); myChart.resize();
}); });
}, },
area_pie: function(){ area_pie: function(data){
// 基本的饼图配置 // 基本的饼图配置
var myChart = echarts.init(document.getElementById('area_pie')); var myChart = echarts.init(document.getElementById('area_pie'));
var option = { var option = {
title: { title: {
text: '取消订单类型', text: '订单区域',
subtext: '', subtext: '',
left: 'right' left: 'right'
}, },
@ -96,11 +110,6 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
trigger: 'item', trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)' formatter: '{a} <br/>{b}: {c} ({d}%)'
}, },
legend: {
orient: 'vertical',
left: 'left',
data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎']
},
color: [ color: [
"#18d1b1", "#18d1b1",
"#3fb1e3", "#3fb1e3",
@ -109,19 +118,18 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
"#c4ebad", "#c4ebad",
"#96dee8" "#96dee8"
], ],
legend: {
orient: 'vertical',
left: 'left',
data: data.map(item => item.name)
},
series: [ series: [
{ {
name: '访问来源', name: '订单区域',
type: 'pie', type: 'pie',
radius: '50%', radius: '50%',
center: ['50%', '60%'], center: ['50%', '60%'],
data: [ data: data,
{ value: 335, name: '直接访问' },
{ value: 310, name: '邮件营销' },
{ value: 234, name: '联盟广告' },
{ value: 135, name: '视频广告' },
{ value: 1548, name: '搜索引擎' }
],
emphasis: { emphasis: {
itemStyle: { itemStyle: {
shadowBlur: 10, shadowBlur: 10,
@ -140,13 +148,13 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
myChart.resize(); myChart.resize();
}); });
}, },
source_pie: function(){ source_pie: function(data){
// 基本的饼图配置 // 基本的饼图配置
var myChart = echarts.init(document.getElementById('source_pie')); var myChart = echarts.init(document.getElementById('source_pie'));
var option = { var option = {
title: { title: {
text: '订单地区', text: '来源店铺',
subtext: '', subtext: '',
left: 'right' left: 'right'
}, },
@ -165,21 +173,15 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
legend: { legend: {
orient: 'vertical', orient: 'vertical',
left: 'left', left: 'left',
data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎'] data: data.map(item => item.name)
}, },
series: [ series: [
{ {
name: '访问来源', name: '来源店铺',
type: 'pie', type: 'pie',
radius: '50%', radius: '50%',
center: ['50%', '60%'], center: ['50%', '60%'],
data: [ data: data,
{ value: 335, name: '直接访问' },
{ value: 310, name: '邮件营销' },
{ value: 234, name: '联盟广告' },
{ value: 135, name: '视频广告' },
{ value: 1548, name: '搜索引擎' }
],
emphasis: { emphasis: {
itemStyle: { itemStyle: {
shadowBlur: 10, shadowBlur: 10,
@ -198,13 +200,13 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
myChart.resize(); myChart.resize();
}); });
}, },
cancel_order_pie: function(){ cancel_order_pie: function(data){
// 基本的饼图配置 // 基本的饼图配置
var myChart = echarts.init(document.getElementById('cancel_order_pie')); var myChart = echarts.init(document.getElementById('cancel_order_pie'));
var option = { var option = {
title: { title: {
text: '订单来源', text: '异常订单占比',
subtext: '', subtext: '',
left: 'right' left: 'right'
}, },
@ -223,7 +225,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
legend: { legend: {
orient: 'vertical', orient: 'vertical',
left: 'left', left: 'left',
data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎'] data: data.map(item => item.name)
}, },
series: [ series: [
{ {
@ -231,13 +233,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
type: 'pie', type: 'pie',
radius: '50%', radius: '50%',
center: ['50%', '60%'], center: ['50%', '60%'],
data: [ data: data,
{ value: 335, name: '直接访问' },
{ value: 310, name: '邮件营销' },
{ value: 234, name: '联盟广告' },
{ value: 135, name: '视频广告' },
{ value: 1548, name: '搜索引擎' }
],
emphasis: { emphasis: {
itemStyle: { itemStyle: {
shadowBlur: 10, shadowBlur: 10,
@ -256,7 +252,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
myChart.resize(); myChart.resize();
}); });
}, },
money_line: function (){ money_line: function (data){
var myChart = echarts.init(document.getElementById('money_line')); var myChart = echarts.init(document.getElementById('money_line'));
var option = { var option = {
@ -283,13 +279,13 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
"#96dee8" "#96dee8"
], ],
legend: { legend: {
data: ['柱状图1', '柱状图2', '折线图'], data: ['订单量', '营销额', '收益','收益率'],
left: 'left' left: 'left'
}, },
xAxis: [ xAxis: [
{ {
type: 'category', type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月'], data: data.xAxis,
axisPointer: { axisPointer: {
type: 'shadow' type: 'shadow'
} }
@ -298,41 +294,51 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts','echarts-th
yAxis: [ yAxis: [
{ {
type: 'value', type: 'value',
name: '柱状图数值', name: '金额',
position: 'left' position: 'left'
}, },
{ {
type: 'value', type: 'value',
name: '折线图数值', name: '单量/百分比',
position: 'right', position: 'right',
min: 0, // 设置右侧y轴的最小值 min: 0, // 设置右侧y轴的最小值
max: 1 // 设置右侧y轴的最大值 max: 100 // 设置右侧y轴的最大值
} }
], ],
series: [ series: [
{ {
name: '柱状图1', name: '订单量',
type: 'bar', type: 'bar',
data: [120, 132, 101, 134, 90, 230], data: data.series.count,
emphasis: {
focus: 'series'
},
yAxisIndex: 1,
barGap: '10%' // 调整柱状图1与柱状图2之间的间距
},
{
name: '营销额',
type: 'bar',
data: data.series.total,
emphasis: { emphasis: {
focus: 'series' focus: 'series'
}, },
barGap: '10%' // 调整柱状图1与柱状图2之间的间距 barGap: '10%' // 调整柱状图1与柱状图2之间的间距
}, },
{ {
name: '柱状图2', name: '收益',
type: 'bar', type: 'bar',
data: [220, 182, 191, 234, 290, 330], data: data.series.performance,
emphasis: { emphasis: {
focus: 'series' focus: 'series'
}, },
barGap: '10%' // 调整柱状图1与柱状图2之间的间距 barGap: '10%' // 调整柱状图1与柱状图2之间的间距
}, },
{ {
name: '折线图', name: '收益率',
type: 'line', type: 'line',
yAxisIndex: 1, yAxisIndex: 1,
data: [0.2, 0.3, 0.4, 0.6, 0.5, 0.8], data: data.series.rate,
smooth: true, smooth: true,
lineStyle: { lineStyle: {
color: '#ff6600' color: '#ff6600'