销售激活统计
This commit is contained in:
parent
5b3d51d90d
commit
0ac9ecbe80
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
namespace App\Domains\Stats\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Card\Card;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Virtual\OrderCard;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
/**
|
||||
* 销售激活统计
|
||||
*/
|
||||
class SoldActivatedController extends Controller
|
||||
{
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* 构造函数,自动注入.
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$conditions = $this->request->all();
|
||||
|
||||
Validator::validate($conditions, [
|
||||
'starttime' => ['required', 'date'],
|
||||
'endtime' => ['required', 'date', 'after_or_equal:starttime'],
|
||||
], [
|
||||
'starttime.required' => '开始时间不能为空',
|
||||
'endtime.required' => '结束时间不能为空',
|
||||
'endtime.after_or_equal' => '结束时间必须大于等于开始时间',
|
||||
]);
|
||||
|
||||
$starttime = Carbon::parse($conditions['starttime'])->startOfMonth();
|
||||
$endtime = Carbon::parse($conditions['endtime'])->endOfMonth();
|
||||
|
||||
$query = OrderCard::leftJoin('cards', 'cards.sim', '=', 'virtual_order_cards.sim')
|
||||
->select([
|
||||
'company_id',
|
||||
DB::raw('count(*) as sells'),
|
||||
DB::raw('count(CASE WHEN virtual_activated_at IS NOT NULL THEN 1 END) as activates'),
|
||||
DB::raw("to_char(virtual_activated_at, 'YYYY-MM') as activated_month"),
|
||||
DB::raw("to_char(virtual_order_cards.created_at, 'YYYY-MM') as order_month"),
|
||||
])->groupBy([
|
||||
'company_id',
|
||||
DB::raw("to_char(virtual_activated_at, 'YYYY-MM')"),
|
||||
DB::raw("to_char(virtual_order_cards.created_at, 'YYYY-MM')")
|
||||
]);
|
||||
|
||||
$query = $query->where('virtual_order_cards.created_at', '>=', $starttime)->where('virtual_order_cards.created_at', '<=', $endtime);
|
||||
|
||||
if (isset($conditions['company_id'])) {
|
||||
$query = $query->where('company_id', $conditions['company_id']);
|
||||
}
|
||||
|
||||
$list = $query->get()->groupBy('company_id');
|
||||
|
||||
$res = [];
|
||||
|
||||
foreach ($list as $company_id => $companyList) {
|
||||
$companyList = $companyList->groupBy('order_month');
|
||||
|
||||
for ($i=0; $i <= $endtime->diffInMonths($starttime); $i++) {
|
||||
$month = $starttime->copy()->addMonths($i)->format('Y-m');
|
||||
|
||||
$monthList = $companyList[$month] ?? collect();
|
||||
|
||||
$monthList = $monthList->keyBy('activated_month');
|
||||
|
||||
$res[$company_id][$month]['order_month'] = $month;
|
||||
$res[$company_id][$month]['sells'] = 0;
|
||||
|
||||
for ($j=0; $j <= $endtime->diffInMonths($starttime); $j++) {
|
||||
$m = $starttime->copy()->addMonths($j)->format('Y-m');
|
||||
|
||||
$item = $monthList[$m] ?? [];
|
||||
|
||||
$res[$company_id][$month]['sells'] += $item['sells'] ?? 0;
|
||||
$res[$company_id][$month][$m] = $item['activates'] ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
$res[$company_id] = array_values($res[$company_id]);
|
||||
}
|
||||
|
||||
return res($res, '销售激活统计', 201);
|
||||
}
|
||||
}
|
@ -17,4 +17,7 @@ $router->group(['prefix' => 'stats', 'as' => 'stats', 'middleware' => ['adminAut
|
||||
$router->get('/company-report/export', ['as' => 'company-report.export', 'uses' => 'CompanyReportController@export']);
|
||||
$router->get('/company-report/detail', ['as' => 'company-report.detail', 'uses' => 'CompanyReportController@detail']);
|
||||
$router->get('/company-report/detail/export', ['as' => 'company-report.detail.export', 'uses' => 'CompanyReportController@detailExport']);
|
||||
|
||||
// 销售激活统计
|
||||
$router->get('/sold-activated', ['as' => 'sold-activated.index', 'uses' => 'SoldActivatedController@index']);
|
||||
});
|
||||
|
@ -265,6 +265,7 @@ class PermissionSeeder extends Seeder
|
||||
['name' => 'stats.order.3', 'title' => '加油包订单统计', 'path' => '/stats/order/3', 'icon' => 'md-color-fill', 'type' => 0, 'open' => 3],
|
||||
['name' => 'stats.company-report.1', 'title' => '用户月报表', 'path' => '/stats/company-report/1', 'icon' => 'ios-contacts', 'type' => 0, 'open' => 3],
|
||||
['name' => 'stats.company-report.2', 'title' => '增值包月报表', 'path' => '/stats/company-report/2', 'icon' => 'md-bonfire', 'type' => 0, 'open' => 3],
|
||||
['name' => 'stats.sold-activated', 'title' => '销售激活统计', 'path' => '/stats/sold-activated', 'icon' => 'md-timer', 'type' => 0, 'open' => 3],
|
||||
],
|
||||
],
|
||||
[
|
||||
|
@ -28,6 +28,7 @@ const routes = [
|
||||
{ path: '/stats/company-count', name: 'StatsCompanyCount', component: load('stats/company-count/index'), meta: { title: '企业统计' } },
|
||||
{ path: '/stats/order/:type', name: 'StatsOrder', component: load('stats/order/index'), meta: { title: '订单统计' } },
|
||||
{ path: '/stats/company-report/:type', name: 'StatsCompanyReport', component: load('stats/company-report/index'), meta: { title: '月报表' } },
|
||||
{ path: '/stats/sold-activated', name: 'SoldActivated', component: load('stats/sold-activated/index'), meta: { title: '销售激活统计' } },
|
||||
{ path: '/artisan/real-sync', name: 'RealSync', component: load('artisan/real-sync/index'), meta: { title: 'RD数据同步' } },
|
||||
{ path: '/flow-pools', name: 'FlowPools', component: load('virtual/flow_pools/index'), meta: { title: '流量池管理' } }
|
||||
]
|
||||
|
104
frontend/src/views/stats/sold-activated/index.vue
Normal file
104
frontend/src/views/stats/sold-activated/index.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="page-wrap">
|
||||
<ui-loading :show="page_loading.show"></ui-loading>
|
||||
<div class="product-content">
|
||||
<div class="nav">
|
||||
<div class="search umar-t5">
|
||||
<AutoComplete @on-search="handleSearchCompanies" placeholder="输入名称进行过滤"></AutoComplete>
|
||||
</div>
|
||||
<div class="box">
|
||||
<CellGroup @on-click="index" v-for="item in companies" :key="item.id">
|
||||
<Cell
|
||||
:name="item.id"
|
||||
:selected="item.id == params.company_id ? true : false"
|
||||
:title="item.name"
|
||||
/>
|
||||
</CellGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-wrap">
|
||||
<div class="page-handle-wrap">
|
||||
<ul class="handle-wraper bd-b">
|
||||
<li class="f-l">
|
||||
<div class="text-exp">
|
||||
<b>{{company.name}}</b>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="search-wrap">
|
||||
<ul class="handle-wraper">
|
||||
<li class="handle-item lh-32">查询时间</li>
|
||||
|
||||
<li class="handle-item">
|
||||
<DatePicker
|
||||
:editable="false"
|
||||
placeholder="开始时间"
|
||||
placement="bottom-start"
|
||||
type="month"
|
||||
v-model.trim="params.starttime"
|
||||
></DatePicker>
|
||||
</li>
|
||||
|
||||
<li class="handle-item lh-32">-</li>
|
||||
|
||||
<li class="handle-item">
|
||||
<DatePicker
|
||||
:editable="false"
|
||||
placeholder="结束时间"
|
||||
placement="bottom-start"
|
||||
type="month"
|
||||
v-model.trim="params.endtime"
|
||||
></DatePicker>
|
||||
</li>
|
||||
|
||||
<li class="f-r">
|
||||
<div class="handle-item">
|
||||
<Button @click="index()" ghost type="primary">立即搜索</Button>
|
||||
</div>
|
||||
<div class="handle-item">
|
||||
<Button @click="resetSearch" ghost type="warning">重置搜索</Button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-list-wrap">
|
||||
<Table :columns="columns" :data="data ? data : []"></Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./js/index.js"></script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page-wrap {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
padding: 0;
|
||||
.product-content {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
.nav {
|
||||
width: 20%;
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
.box {
|
||||
margin-top: 10px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
height: calc(100% - 5px);
|
||||
}
|
||||
}
|
||||
.info-wrap {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
109
frontend/src/views/stats/sold-activated/js/index.js
Normal file
109
frontend/src/views/stats/sold-activated/js/index.js
Normal file
@ -0,0 +1,109 @@
|
||||
export default {
|
||||
name: 'SoldActivated',
|
||||
data() {
|
||||
return {
|
||||
params: {
|
||||
company_id: '',
|
||||
starttime: this.moment().startOf('year').format('YYYY-MM'),
|
||||
endtime: this.moment().subtract('1', 'months').format('YYYY-MM')
|
||||
},
|
||||
search: {
|
||||
show: false
|
||||
},
|
||||
companies: [],
|
||||
company: { id: 0, name: '请选择企业' },
|
||||
data: [],
|
||||
columns: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initCompleteCompanies().then(res => {
|
||||
this.companies = res.filter(function(item) {
|
||||
return item.status === 0;
|
||||
});
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
setColumns() {
|
||||
let columns = [
|
||||
{
|
||||
title: '销售时间',
|
||||
key: 'order_month',
|
||||
minWidth: 110
|
||||
},
|
||||
{
|
||||
title: '销售数',
|
||||
key: 'sells',
|
||||
minWidth: 110
|
||||
}
|
||||
];
|
||||
|
||||
for (let i = 0; i <= this.moment(this.params.endtime).diff(this.moment(this.params.starttime), 'months'); i++) {
|
||||
columns.push({
|
||||
title: this.moment(this.params.starttime).add(i, 'months').format('YYYY-MM'),
|
||||
key: this.moment(this.params.starttime).add(i, 'months').format('YYYY-MM'),
|
||||
minWidth: 110
|
||||
});
|
||||
}
|
||||
|
||||
this.columns = columns;
|
||||
},
|
||||
|
||||
/**
|
||||
* [index 列表]
|
||||
* @param {Number} company_id [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
index(company_id = null) {
|
||||
if (company_id) {
|
||||
this.params.company_id = company_id;
|
||||
this.company = this.companies.find(item => {
|
||||
return item.id === company_id;
|
||||
});
|
||||
}
|
||||
|
||||
if (this.params.company_id === '') {
|
||||
return this.$Message.error('请先选择企业');
|
||||
}
|
||||
|
||||
this.setColumns();
|
||||
this.isShowLoading(true);
|
||||
|
||||
let params = JSON.parse(JSON.stringify(this.params));
|
||||
|
||||
params.starttime = this.moment(params.starttime).format('YYYY-MM');
|
||||
params.endtime = this.moment(params.endtime).format('YYYY-MM');
|
||||
|
||||
service.get('api/stats/sold-activated', {
|
||||
params
|
||||
}).then(res => {
|
||||
this.isShowLoading(false);
|
||||
if (res.code == 0) {
|
||||
this.data = res.data[this.params.company_id];
|
||||
}
|
||||
}).catch(() => {
|
||||
this.isShowLoading(false);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* [request 刷新]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
request() {
|
||||
this.index();
|
||||
},
|
||||
resetSearch() {
|
||||
this.params.starttime = this.moment().startOf('year').format('YYYY-MM');
|
||||
this.params.endtime = this.moment().subtract('1', 'months').format('YYYY-MM');
|
||||
this.index();
|
||||
},
|
||||
handleSearchCompanies(value) {
|
||||
this.handleCompleteCompanies(value).then(res => {
|
||||
this.companies = res.filter(item => {
|
||||
return item.status === 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
2
public/css/chunk-64227684.c6156de6.css
Normal file
2
public/css/chunk-64227684.c6156de6.css
Normal file
File diff suppressed because one or more lines are too long
2
public/js/app.7c3bbc13.js
Normal file
2
public/js/app.7c3bbc13.js
Normal file
File diff suppressed because one or more lines are too long
1
public/js/app.7c3bbc13.js.map
Normal file
1
public/js/app.7c3bbc13.js.map
Normal file
File diff suppressed because one or more lines are too long
2
public/js/app.7c6af744.js
Normal file
2
public/js/app.7c6af744.js
Normal file
File diff suppressed because one or more lines are too long
1
public/js/app.7c6af744.js.map
Normal file
1
public/js/app.7c6af744.js.map
Normal file
File diff suppressed because one or more lines are too long
2
public/js/chunk-07a274ec.20f6d59e.js
Normal file
2
public/js/chunk-07a274ec.20f6d59e.js
Normal file
File diff suppressed because one or more lines are too long
1
public/js/chunk-07a274ec.20f6d59e.js.map
Normal file
1
public/js/chunk-07a274ec.20f6d59e.js.map
Normal file
File diff suppressed because one or more lines are too long
15
public/js/chunk-64227684.83fe7739.js
Normal file
15
public/js/chunk-64227684.83fe7739.js
Normal file
File diff suppressed because one or more lines are too long
1
public/js/chunk-64227684.83fe7739.js.map
Normal file
1
public/js/chunk-64227684.83fe7739.js.map
Normal file
File diff suppressed because one or more lines are too long
15
public/js/chunk-64227684.f1668692.js
Normal file
15
public/js/chunk-64227684.f1668692.js
Normal file
File diff suppressed because one or more lines are too long
1
public/js/chunk-64227684.f1668692.js.map
Normal file
1
public/js/chunk-64227684.f1668692.js.map
Normal file
File diff suppressed because one or more lines are too long
2
public/js/chunk-996b1e80.1e853bf4.js
Normal file
2
public/js/chunk-996b1e80.1e853bf4.js
Normal file
File diff suppressed because one or more lines are too long
1
public/js/chunk-996b1e80.1e853bf4.js.map
Normal file
1
public/js/chunk-996b1e80.1e853bf4.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=\favicon.ico><script src=\config.js></script><title></title><link href=/css/chunk-01a502ce.e5be0e8f.css rel=prefetch><link href=/css/chunk-996b1e80.5cadf3d0.css rel=prefetch><link href=/js/chunk-00ae0766.3874cd10.js rel=prefetch><link href=/js/chunk-01a502ce.b2793a6c.js rel=prefetch><link href=/js/chunk-07a274ec.c3ad5dec.js rel=prefetch><link href=/js/chunk-996b1e80.d3b45e46.js rel=prefetch><link href=/css/app.d71a8195.css rel=preload as=style><link href=/css/chunk-vendors.3c3b2e85.css rel=preload as=style><link href=/js/app.6a9ce39d.js rel=preload as=script><link href=/js/chunk-vendors.ed6443e8.js rel=preload as=script><link href=/css/chunk-vendors.3c3b2e85.css rel=stylesheet><link href=/css/app.d71a8195.css rel=stylesheet></head><body><noscript><strong>很抱歉,如果没有启用JavaScript,程序不能正常工作,若要继续使用请启用它。</strong></noscript><div id=app></div><script src=/js/chunk-vendors.ed6443e8.js></script><script src=/js/app.6a9ce39d.js></script></body></html>
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=\favicon.ico><script src=\config.js></script><title></title><link href=/css/chunk-64227684.c6156de6.css rel=prefetch><link href=/css/chunk-996b1e80.5cadf3d0.css rel=prefetch><link href=/js/chunk-00ae0766.3874cd10.js rel=prefetch><link href=/js/chunk-07a274ec.20f6d59e.js rel=prefetch><link href=/js/chunk-64227684.f1668692.js rel=prefetch><link href=/js/chunk-996b1e80.1e853bf4.js rel=prefetch><link href=/css/app.d71a8195.css rel=preload as=style><link href=/css/chunk-vendors.3c3b2e85.css rel=preload as=style><link href=/js/app.7c6af744.js rel=preload as=script><link href=/js/chunk-vendors.ed6443e8.js rel=preload as=script><link href=/css/chunk-vendors.3c3b2e85.css rel=stylesheet><link href=/css/app.d71a8195.css rel=stylesheet></head><body><noscript><strong>很抱歉,如果没有启用JavaScript,程序不能正常工作,若要继续使用请启用它。</strong></noscript><div id=app></div><script src=/js/chunk-vendors.ed6443e8.js></script><script src=/js/app.7c6af744.js></script></body></html>
|
Loading…
x
Reference in New Issue
Block a user