订单统计

This commit is contained in:
邓皓元 2019-01-08 19:01:15 +08:00
parent 2e5f5240af
commit d56d4e4047
29 changed files with 714 additions and 460 deletions

View File

@ -12,6 +12,7 @@ use Dipper\Excel\Concerns\WithEvents;
use Dipper\Excel\Events\BeforeExport;
use Illuminate\Filesystem\Filesystem;
use Dipper\Excel\Events\BeforeWriting;
use App\Exceptions\NotAllowedException;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Storage;
use Dipper\Excel\Concerns\ShouldAutoSize;
@ -24,6 +25,7 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize
public static $classes = [
\App\Domains\Virtual\Exports\CardExport::class => '客户列表',
\App\Domains\Stats\Exports\CompanyCountExport::class => '企业统计',
\App\Domains\Stats\Exports\OrderExport::class => '订单统计',
];
public $sn;
@ -141,7 +143,7 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize
*/
public function title(): string
{
$title = self::$classes[get_class($this)];
$title = $this->tag();
if (($conditions = $this->conditions) && $conditions['starttime'] && $conditions['endtime']) {
$title .= Carbon::parse($conditions['starttime'])->format('Ymd') . '-' . Carbon::parse($conditions['endtime'])->format('Ymd');
@ -167,7 +169,7 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize
*/
private function filename(): string
{
$title = self::$classes[get_class($this)];
$title = $this->tag();
$filename = $title . date('YmdHis');
@ -181,15 +183,10 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize
*/
private function tag()
{
$className = get_class($this);
if (!$tag = self::$classes[get_class($this)]) {
throw new NotAllowedException('类型不允许');
}
return self::transformerClassName($className);
}
public static function transformerClassName($className)
{
$baseName = array_last(explode('\\', $className));
return str_replace('Export', '', $baseName);
return $tag;
}
}

View File

@ -39,14 +39,6 @@ class ExportService extends Service
$exports = $this->exportRepository->withConditions($conditions)->applyConditions()->paginate($limit);
$classes = AbstractExport::$classes;
foreach ($classes as $key => $value) {
$new = AbstractExport::transformerClassName($key);
$classes[$new] = $value;
unset($classes[$key]);
}
$status = app(Dicts::class)->get('export_status');
$exports->transform(function ($item) use ($classes, $status) {
@ -68,7 +60,7 @@ class ExportService extends Service
return [
'id' => $item->id,
'tag_name' => $classes[$item['tag']] ?? '未知',
'tag' => $item->tag,
'filesize' => human_filesize($item->filesize),
'dateline' => !$item->conditions['starttime'] ? '所有' : $item->conditions['starttime'] . ' 至 ' . $item->conditions['endtime'],
'conditions' => $conditions,

View File

@ -0,0 +1,93 @@
<?php
namespace App\Domains\Stats\Exports;
use App\Core\AbstractExport;
use Dipper\Excel\Concerns\Exportable;
use Dipper\Excel\Concerns\WithHeadings;
use Dipper\Excel\Concerns\FromCollection;
use Dipper\Excel\Concerns\WithHeadingRow;
use App\Domains\Stats\Services\OrderService;
class OrderExport extends AbstractExport implements FromCollection, WithHeadings, WithHeadingRow
{
public $conditions;
public static $types = ['销售', '续费', '续费包', '加油包'];
public function __construct(array $conditions = [])
{
$this->conditions = $conditions;
parent::__construct();
}
public function collection()
{
set_time_limit(-1);
$orders = app(OrderService::class)->index($this->conditions)->map(function ($item) {
return collect([
'company_name' => $item->company_name,
'package_name' => $item->package_name,
'pay_channel_name' => $item->pay_channel_name,
'unit_price' => $item->unit_price,
'counts' => $item->counts,
'custom_price' => $item->custom_price,
]);
});
$orders->push([
'总计',
'',
'',
'',
array_sum($orders->pluck('counts')->toArray()) ?: 0,
array_sum($orders->pluck('custom_price')->toArray()) ?: 0,
]);
return $orders;
}
public function headings(): array
{
return [
'企业名称',
'套餐名称',
'支付方式',
'单价',
'数量',
'总金额',
];
}
/**
* 表格标题
*
* @return string
*/
public function title(): string
{
$title = parent::title();
$title = self::$types[$this->conditions['type']] . $title;
return $title;
}
/**
* 文件名称
*
* @return string
*/
private function filename(): string
{
$title = self::$classes[get_class($this)];
$filename = $title . date('YmdHis');
$filename = self::$types[$this->conditions['type']] . $filename;
return "export/{$filename}.xlsx";
}
}

View File

@ -1,9 +1,10 @@
<?php
namespace App\Domains\Stats\Http\Controllers;
use App\Core\Controller;
use Illuminate\Http\Request;
use App\Domains\Stats\Exports\OrderExport;
use App\Domains\Stats\Services\OrderService;
use App\Domains\Export\Services\ExportService;
class OrderController extends Controller
{
@ -33,6 +34,26 @@ class OrderController extends Controller
return res($res, '订单统计', 201);
}
/**
* 导出.
*
* @return \Illuminate\Http\Response
*/
public function export()
{
$conditions = $this->request->all();
$conditions['limit'] = 0;
try {
$export = new OrderExport($conditions);
$url = ExportService::store($export, $this->disk);
} catch (\Exception $e) {
throw $e;
}
return res($url, '导出成功', 201);
}
/**
* 统计明细.
@ -47,4 +68,24 @@ class OrderController extends Controller
return res($res, '统计明细', 201);
}
/**
* 统计明细导出.
*
* @return \Illuminate\Http\Response
*/
public function detailExport()
{
$conditions = $this->request->all();
$conditions['limit'] = 0;
try {
$export = new OrderExport($conditions);
$url = ExportService::store($export, $this->disk);
} catch (\Exception $e) {
throw $e;
}
return res($url, '导出成功', 201);
}
}

View File

@ -8,5 +8,8 @@ $router->group(['prefix' => 'stats', 'as' => 'stats', 'middleware' => ['adminAut
// 订单统计
$router->get('/order', ['as' => 'order.index', 'uses' => 'OrderController@index']);
$router->get('/order/export', ['as' => 'order.export', 'uses' => 'OrderController@export']);
$router->get('/order/detail', ['as' => 'order.detail', 'uses' => 'OrderController@detail']);
$router->get('/order/detail/export', ['as' => 'order.detail.export', 'uses' => 'OrderController@detailExport']);
});

View File

@ -1,6 +1,7 @@
<?php
namespace App\Domains\Stats\Services;
use App\Dicts;
use App\Core\Service;
use Illuminate\Support\Facades\DB;
use App\Exceptions\NotAllowedException;
@ -91,12 +92,17 @@ class OrderService extends Service
$companies = $this->companyRepository->withTrashed()->get()->pluck('name', 'id')->toArray();
$packages = $this->packageRepository->withTrashed()->get()->keyBy('id')->toArray();
$carrierOperators = app(Dicts::class)->get('carrier_operator');
$cards = $repository->with('order:id,unit_price,pay_channel,order_at')->withConditions($conditions)->applyConditions()->paginate($conditions['limit']);
$cards->map(function ($item) use ($companies, $packages) {
$item->company_name = $packages[$item->company_id];
$item->package_name = $packages[$item->package_id]['name'];
$item->service_months = $packages[$item->package_id]['service_months'];
$cards->map(function ($item) use ($companies, $packages, $carrierOperators) {
$package = $packages[$item->package_id];
$item->company_name = $companies[$item->company_id];
$item->package_name = $package['name'];
$item->carrier_operator_name = $carrierOperators[$package['carrier_operator']];
$item->service_months = $package['service_months'];
$item->unit_price = $item->order['unit_price'];
$item->pay_channel_name = CommonService::namePayChannel($item->order['pay_channel']);
$item->order_at = $item->order['order_at'];

View File

@ -68,7 +68,7 @@ class LogSync extends Command
$value = (array)$value;
$package = $this->getPackage($value['content']);
$unit_price = intval($value['sale_account'] * 100);
$unit_price = intval($value['order_account'] * 100);
$custom_price = intval($value['order_account'] * 100);
$product = $this->getProduct($package, $value['company'], $unit_price);
$type = self::$types[$value['type']];

View File

@ -19,7 +19,7 @@ class ProductSync extends Command
{
$packages = app(PackageRepository::class)->where('type', 0)->get()->keyBy('sn')->toArray();
$fields = ['company', 'content', 'sale_account'];
$fields = ['company', 'content', 'order_account'];
$list = DB::connection('vd_old')->table('ckb_custom_handle_log')->select($fields)
->where('type', 13)
@ -33,13 +33,13 @@ class ProductSync extends Command
throw new \Exception('套餐不存在');
}
$base_price = intval($value['sale_account'] * 100) ;
$base_price = intval($value['order_account'] * 100) ;
$products[] = [
'sn' => strtoupper($package['sn'] . '_' . $value['company'] . '_' . $base_price),
'company_id' => $value['company'],
'package_id' => $package['id'],
'name' => $package['name'] . ' ' . $value['sale_account'],
'name' => $package['name'] . ' ' . $value['order_account'],
'base_price' => $base_price,
'renewal_price' => $base_price,
'created_at' => date('Y-m-d H:i:s'),

View File

@ -25,7 +25,8 @@ const routes = [
{ path: '/cards', name: 'Cards', component: load('virtual/cards/index'), meta: { title: '客户列表' } },
{ path: '/exports', name: 'StatsExports', component: load('exports/index'), meta: { title: '导出记录' } },
{ 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/order/:type', name: 'StatsOrder', component: load('stats/order/index'), meta: { title: '订单统计' } },
{ path: '/stats/order/detail/:type', name: 'StatsOrderDetail', component: load('stats/order/detail'), meta: { title: '订单明细' } }
]
},
{ path: '*', redirect: { path: '/home' } }

View File

@ -1,139 +0,0 @@
const menus = [
{
title: '系统设置',
link: '',
icon: 'md-albums',
children: [
{
title: '菜单管理',
link: '/menus',
icon: '',
children: []
},
{
title: '角色管理',
link: '/roles',
icon: '',
children: []
},
{
title: '开发者管理',
link: '/developer',
icon: '',
children: []
}
]
},
{
title: '用户管理',
link: '',
icon: 'md-analytics',
children: [
{
title: '用户权限管理',
link: '/jurisdiction',
icon: '',
children: []
},
{
title: '管理员管理',
link: '/admin',
icon: '',
children: []
},
{
title: '教师管理',
link: '/teacher',
icon: '',
children: []
},
{
title: '学员管理',
link: '/student',
icon: '',
children: []
}
]
},
{
title: '测评管理',
link: '',
icon: 'ios-aperture',
children: [
{
title: '专注力测评管理',
link: '/evaluation',
icon: '',
children: []
},
{
title: '专注力题库管理',
link: '/questions',
icon: '',
children: []
}
]
},
{
title: '课程管理',
link: '',
icon: 'ios-barcode',
children: [
{
title: '课程分类管理',
link: '/course_classify',
icon: '',
children: []
},
{
title: '课程项目管理',
link: '/course_project',
icon: '',
children: []
},
{
title: '学员排课管理',
link: '/course_work',
icon: '',
children: []
}
]
},
{
title: '教学管理',
link: '',
icon: 'md-beer',
children: [
{
title: '学员课业管理',
link: '/lessons',
icon: '',
children: []
},
{
title: '请假调课管理',
link: '/leave',
icon: '',
children: []
},
{
title: '课业反馈管理',
link: '/feedback',
icon: '',
children: []
},
{
title: '家庭作业素材',
link: '/work_material',
icon: '',
children: []
}
]
}
];
export default menus;

View File

@ -144,7 +144,7 @@ export function objectDot(object, prepend = '') {
*/
export function sumBy(array, key) {
return array.map((item) => {
return item[key];
return Number(item[key]);
}).reduce((acc, cur) => {
return acc + cur;
});

View File

@ -28,8 +28,12 @@
<ul class="handle-wraper">
<li class="handle-item w-250">
<Select clearable placeholder="类型" v-model="other.tag">
<Option value="Card">客户列表</Option>
<Option value="CompanyCount">企业统计</Option>
<Option value="客户列表"></Option>
<Option value="企业统计"></Option>
<Option value="销售订单统计"></Option>
<Option value="续费订单统计"></Option>
<Option value="续费包订单统计"></Option>
<Option value="加油包订单统计"></Option>
</Select>
</li>

View File

@ -30,7 +30,7 @@ export default {
},
{
title: '类型',
key: 'tag_name',
key: 'tag',
width: 120
},
{

View File

@ -1,3 +1,13 @@
<template>
<router-view></router-view>
</template>
<router-view :key="key"></router-view>
</template>
<script>
export default {
computed: {
key() {
return this.$route.fullPath;
}
}
};
</script>

View File

@ -1,102 +1,104 @@
<template>
<component :is="apps_info.theme"></component>
<component :is="apps_info.theme"></component>
</template>
<script>
export default {
components: {
themeOne: resolve => require(['views/layout/theme/one'], resolve),
themeTwo: resolve => require(['views/layout/theme/two'], resolve)
},
data() {
return {}
},
watch: {
'$route'(to, from) {
this.init();
},
'breadcrumb': {
deep: true,
handler(data) {
// console.log(data);
//
const mids = data.map(item => Number(item.id)).filter(id => id);
this.$store.commit('SET_ACTIVES', this.deepClone(mids));
}
},
'tagnavs': {
deep: true,
handler(data) {
//
this.$store.dispatch('getCachPage');
}
}
},
created() {
this.indexPermissions(); //
},
methods: {
init() {
const mid = this.$route.query.mid;
if (mid !== undefined) {
this.menuChange(mid);
}
this.$store.dispatch('getCurrentNodes'); //
this.getBreadcrumb();
},
/**
* [indexPermissions 获取菜单列表]
* @return {[type]} [description]
*/
indexPermissions() {
this.$store.dispatch('getSiteInfo').then(res => {
if (res.code == 0) {
//
this.init();
}
})
},
/**
* [menuChange 路由变化将菜单添加到tagnavs变量中]
* @param {[type]} mid [description]
* @return {[type]} [description]
*/
menuChange(mid) {
this.$nextTick(() => {
const route = this.$route;
const cur_permission = (this.permissions_object && this.permissions_object[mid]) ? this.permissions_object[mid] : null;
let has = true;
if (has) {
for (let i = 0, len = this.tagnavs.length; i < len; i++) {
if (this.tagnavs[i].id == mid) {
has = false;
break;
}
;
}
}
if (has) {
let obj = {
id: mid,
path: route.path,
name: route.name,
query: this.deepClone(route.query),
params: this.deepClone(route.params),
title: cur_permission ? cur_permission.title : route.meta.title
}
this.$store.commit('SET_TAGNAVS', obj);
}
});
},
//
getBreadcrumb() {
this.$store.dispatch('getBreadcrumb');
},
export default {
components: {
themeOne: resolve => require(['views/layout/theme/one'], resolve),
themeTwo: resolve => require(['views/layout/theme/two'], resolve)
},
data() {
return {};
},
watch: {
$route(to, from) {
this.init();
},
breadcrumb: {
deep: true,
handler(data) {
// console.log(data);
//
const mids = data.map(item => Number(item.id)).filter(id => id);
this.$store.commit('SET_ACTIVES', this.deepClone(mids));
}
}
},
tagnavs: {
deep: true,
handler(data) {
//
this.$store.dispatch('getCachPage');
}
}
},
created() {
this.indexPermissions(); //
},
methods: {
init() {
const mid = this.$route.query.mid;
if (mid !== undefined) {
this.menuChange(mid);
}
this.$store.dispatch('getCurrentNodes'); //
this.getBreadcrumb();
},
/**
* [indexPermissions 获取菜单列表]
* @return {[type]} [description]
*/
indexPermissions() {
this.$store.dispatch('getSiteInfo').then(res => {
if (res.code == 0) {
//
this.init();
}
});
},
/**
* [menuChange 路由变化将菜单添加到tagnavs变量中]
* @param {[type]} mid [description]
* @return {[type]} [description]
*/
menuChange(mid) {
this.$nextTick(() => {
const route = this.$route;
const cur_permission =
this.permissions_object && this.permissions_object[mid]
? this.permissions_object[mid]
: null;
let has = true;
if (has) {
for (let i = 0, len = this.tagnavs.length; i < len; i++) {
if (this.tagnavs[i].id == mid) {
has = false;
break;
}
}
}
if (has) {
let obj = {
id: mid,
path: route.path,
name: route.name,
query: this.deepClone(route.query),
params: this.deepClone(route.params),
title: cur_permission ? cur_permission.title : route.meta.title
};
this.$store.commit('SET_TAGNAVS', obj);
}
});
},
//
getBreadcrumb() {
this.$store.dispatch('getBreadcrumb');
}
}
};
</script>

View File

@ -5,16 +5,7 @@
<img :src="CONFIG.logo_big" class="big" v-else>
</div>
<div class="nav-wrap" v-if="left_menu.list.length">
<Menu
:active-name="left_menu.active_name"
:open-names="left_menu.open_names"
@on-select="menuChange"
accordion
ref="sideMenu"
theme="dark"
v-show="!collapsed"
width="auto"
>
<Menu :active-name="left_menu.active_name" :open-names="left_menu.open_names" @on-select="menuChange" accordion ref="sideMenu" theme="dark" v-show="!collapsed" width="auto">
<template v-for="(item,index) in left_menu.list">
<side-menu-item :menu="item" v-if="item.menus && item.menus.length"></side-menu-item>
@ -35,7 +26,7 @@
</template>
<script>
//
//
import sideMenuItem from "views/layout/menu/side_menu_item";
import collapsedMenu from "views/layout/menu/collapsed_menu";
@ -69,13 +60,13 @@ export default {
menuChange(mid) {
const menu = this.permissions_object[mid];
switch (menu.open) {
case 0: //iframe
this.$router.push({ path: "/iframe", query: { mid: menu.id } });
case 0: // iframe
this.$router.push({ path: '/iframe', query: { mid: menu.id } });
break;
case 1: //
case 1: //
window.open(menu.path);
break;
case 2: //
case 2: //
const top = (window.outerHeight - menu.height) / 2;
const left = (window.outerWidth - menu.width) / 2;
window.open(
@ -84,12 +75,11 @@ export default {
`width=${menu.width},height=${menu.height},top=${top},left=${left}`
);
break;
case 3: //vue
case 3: // vue
this.$router.push({ path: menu.path, query: { mid: menu.id } });
break;
}
}
}
};
</script>
</script>

View File

@ -1,79 +1,81 @@
<template>
<div class="layout">
<Layout>
<Sider class="layout-sider" hide-trigger collapsible :width="256" :collapsed-width="64" v-model="collapsed">
<side-menu :collapsed="collapsed"></side-menu>
</Sider>
<div class="layout">
<Layout>
<Sider :collapsed-width="64" :width="256" class="layout-sider" collapsible hide-trigger v-model="collapsed">
<side-menu :collapsed="collapsed"></side-menu>
</Sider>
<Layout id="layout">
<Header class="layout-head" :style="left">
<header-bar :collapsed.sync="collapsed">
<ui-breadcrumb></ui-breadcrumb>
</header-bar>
</Header>
<Layout id="layout">
<Header :style="left" class="layout-head">
<header-bar :collapsed.sync="collapsed">
<ui-breadcrumb></ui-breadcrumb>
</header-bar>
</Header>
<Content>
<Layout v-if="apps_info.show_navs && tagnavs.length">
<div class="tag-nav-wrapper" :style="left">
<tag-nav></tag-nav>
</div>
</Layout>
<Content>
<Layout v-if="apps_info.show_navs && tagnavs.length">
<div :style="left" class="tag-nav-wrapper">
<tag-nav></tag-nav>
</div>
</Layout>
<Content class="layout-content-wrap" :style="top">
<div class="layout-content">
<keep-alive :include="cache_page">
<router-view></router-view>
</keep-alive>
</div>
</Content>
</Content>
</Layout>
<Content :style="top" class="layout-content-wrap">
<div class="layout-content">
<keep-alive :include="cache_page">
<router-view :key="key"></router-view>
</keep-alive>
</div>
</Content>
</Content>
</Layout>
</div>
</Layout>
</div>
</template>
<script>
export default{
data(){
return{
collapsed: false, //
}
},
components: {
sideMenu: resolve => require(['views/layout/menu/side_menu'], resolve),
headerBar: resolve => require(['views/layout/header_bar/header_bar'], resolve),
tagNav: resolve => require(['views/layout/tags_nav/index'], resolve),
},
watch:{
'$route'(to,from){
}
},
computed:{
left(){
return {
paddingLeft: this.collapsed?'64px':'256px'
}
},
top(){ //
if(this.apps_info.show_navs && this.tagnavs.length){
return {
paddingTop: '104px'
}
}else{
return {
paddingTop: '64px'
}
}
},
export default {
data() {
return {
collapsed: false //
};
},
components: {
sideMenu: resolve => require(["views/layout/menu/side_menu"], resolve),
headerBar: resolve =>
require(["views/layout/header_bar/header_bar"], resolve),
tagNav: resolve => require(["views/layout/tags_nav/index"], resolve)
},
watch: {
$route(to, from) {}
},
computed: {
key() {
return this.$route.fullPath;
},
left() {
return {
paddingLeft: this.collapsed ? "64px" : "256px"
};
},
top() {
//
if (this.apps_info.show_navs && this.tagnavs.length) {
return {
paddingTop: "104px"
};
} else {
return {
paddingTop: "64px"
};
}
}
}
}
};
</script>
<style scoped>
>>> .ivu-layout-has-sider{
width: 100%;
height: 100%;
}
>>> .ivu-layout-has-sider {
width: 100%;
height: 100%;
}
</style>

View File

@ -1,75 +1,80 @@
<template>
<div class="layout">
<Layout>
<Sider class="layout-sider" hide-trigger collapsible :width="256" :collapsed-width="64" v-model="collapsed">
<side-menu :collapsed="collapsed"></side-menu>
</Sider>
<div class="layout">
<Layout>
<Sider :collapsed-width="64" :width="256" class="layout-sider" collapsible hide-trigger v-model="collapsed">
<side-menu :collapsed="collapsed"></side-menu>
</Sider>
<Layout id="layout">
<Header class="layout-head theme-two" :style="left">
<header-bar :collapsed.sync="collapsed">
<top-menu></top-menu>
</header-bar>
</Header>
<Layout id="layout">
<Header :style="left" class="layout-head theme-two">
<header-bar :collapsed.sync="collapsed">
<top-menu></top-menu>
</header-bar>
</Header>
<Content>
<Layout v-if="apps_info.show_navs && tagnavs.length">
<div class="tag-nav-wrapper" :style="left">
<tag-nav></tag-nav>
</div>
</Layout>
<Content>
<Layout v-if="apps_info.show_navs && tagnavs.length">
<div :style="left" class="tag-nav-wrapper">
<tag-nav></tag-nav>
</div>
</Layout>
<Content class="layout-content-wrap" :style="top">
<div class="layout-content">
<keep-alive :include="cache_page">
<router-view></router-view>
</keep-alive>
</div>
</Content>
</Content>
</Layout>
<Content :style="top" class="layout-content-wrap">
<div class="layout-content">
<keep-alive :include="cache_page">
<router-view :key="key"></router-view>
</keep-alive>
</div>
</Content>
</Content>
</Layout>
</div>
</Layout>
</div>
</template>
<script>
export default{
data(){
return{
collapsed: false, //
}
},
components: {
sideMenu: resolve => require(['views/layout/menu/side_menu'], resolve),
topMenu: resolve => require(['views/layout/menu/top_menu'], resolve),
headerBar: resolve => require(['views/layout/header_bar/header_bar'], resolve),
tagNav: resolve => require(['views/layout/tags_nav/index'], resolve),
},
computed:{
left(){
return {
paddingLeft: this.collapsed?'64px':'256px'
}
},
top(){ //
if(this.apps_info.show_navs && this.tagnavs.length){
return {
paddingTop: '104px'
}
}else{
return {
paddingTop: '64px'
}
}
},
export default {
data() {
return {
//
collapsed: false
};
},
components: {
sideMenu: resolve => require(['views/layout/menu/side_menu'], resolve),
topMenu: resolve => require(['views/layout/menu/top_menu'], resolve),
headerBar: resolve =>
require(['views/layout/header_bar/header_bar'], resolve),
tagNav: resolve => require(['views/layout/tags_nav/index'], resolve)
},
computed: {
key() {
return this.$route.fullPath;
},
left() {
return {
paddingLeft: this.collapsed ? '64px' : '256px'
};
},
top() {
//
if (this.apps_info.show_navs && this.tagnavs.length) {
return {
paddingTop: '104px'
};
} else {
return {
paddingTop: '64px'
};
}
}
}
}
};
</script>
<style scoped>
>>> .ivu-layout-has-sider{
width: 100%;
height: 100%;
}
>>> .ivu-layout-has-sider {
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,36 @@
<template>
<Modal :footer-hide="true" :mask-closable="false" @on-visible-change="visibleChange" title="详情" v-model="my_show" width="1200">
<div class="page-detail-wrap">
<ui-loading :show="page_loading.show"></ui-loading>
<div class="page-handle-wrap">
<ul class="handle-wraper bd-b">
<li class="f-l">
<div class="text-exp">
<b>全部信息</b>
</div>
</li>
<li class="f-r">
<div class="handle-item">
<Button @click="index(1)" icon="md-refresh">刷新</Button>
</div>
<div class="handle-item">
<Button @click="exportExcel" icon="md-download">导出</Button>
</div>
</li>
</ul>
<div class="page-list-wrap">
<Table :columns="columns" :data="list_data ? list_data.data : []" stripe width="1150"></Table>
</div>
<div class="page-turn-wrap" v-if="list_data">
<Page :current="Number(list_data.current_page)" :page-size="Number(list_data.per_page)" :total="Number(list_data.total)" @on-change="index" show-elevator show-total></Page>
</div>
</div>
</div>
</Modal>
</template>
<script src="./js/detail.js"></script>

View File

@ -33,13 +33,13 @@
</li>
<li class="handle-item w-250">
<AutoComplete @on-search="handleCompletePackages" icon="ios-search" placeholder="套餐名称" v-model.trim="params.package_name">
<AutoComplete @on-search="handleCompletePackages" icon="ios-search" placeholder="套餐名称" v-model.trim="options.package_name">
<Option :key="item.id" :value="item.name" v-for="item in completeHandledPackages">{{ item.name }}</Option>
</AutoComplete>
</li>
<li class="handle-item w-250">
<Select clearable placeholder="支付方式" v-model="params.pay_channel">
<Select clearable placeholder="支付方式" v-model="options.pay_channel">
<Option :value="'bank'">银行转账</Option>
<Option :value="'wx'">微信</Option>
<Option :value="'alipay'">支付宝</Option>
@ -68,7 +68,7 @@
<Table :columns="columns" :data="data" :height="page.limit > 12 ? 610 : ''" ref="table" stripe>
<template class="table-footer" slot="footer">
<colgroup class="table-footer-colgroup"></colgroup>
<thead class="ivu-table">
<thead class="ivu-table" v-show="data.length">
<tr>
<th>
<div class="ivu-table-cell">总计</div>
@ -86,7 +86,10 @@
<div class="ivu-table-cell">{{stats.counts}}</div>
</th>
<th>
<div class="ivu-table-cell">{{stats.custom_price}}</div>
<div class="ivu-table-cell">{{Number(stats.custom_price).toFixed(2)}}</div>
</th>
<th>
<div class="ivu-table-cell"></div>
</th>
<th rowspan="1" v-if="page.limit > 12"></th>
</tr>
@ -108,6 +111,8 @@
show-total
></Page>
</div>
<ui-detail :show.sync="detailObj.show" :options="detailObj.options" :list="detailObj.list"></ui-detail>
</div>
</template>

View File

@ -0,0 +1,111 @@
export default {
name: 'StatsOrderDetail',
props: {
show: {
type: Boolean,
default: false
},
options: {
type: Object,
default: {
type: null,
order_id: null,
orderBy: 'id',
sortedBy: 'asc'
}
},
list: {
type: Object,
default: null
}
},
watch: {
show(bool) {
this.my_show = bool;
},
list(obj) {
this.list_data = obj;
}
},
data() {
return {
my_show: false,
list_data: null,
columns: [{
title: 'SIM',
key: 'sim',
width: 150
},
{
title: '企业名称',
key: 'company_name',
width: 300
},
{
title: '套餐名称',
key: 'package_name',
width: 150
},
{
title: '套餐周期',
key: 'service_months',
width: 120
},
{
title: '支付方式',
key: 'pay_channel_name',
width: 120
},
{
title: '价格',
key: 'unit_price',
width: 120
},
{
title: '订单时间',
key: 'order_at',
width: 170
}
]
};
},
methods: {
visibleChange(bool) {
this.$emit('update:show', bool);
},
/**
* [index 列表]
* @param {Number} page [description]
* @return {[type]} [description]
*/
index(page) {
this.isShowLoading(true);
let params = this.options;
params.page = page;
service.get('api/stats/order/detail', { params }).then(res => {
this.isShowLoading(false);
if (res.code == 0) {
this.list_data = res.data;
}
}).catch(() => {
this.isShowLoading(false);
});
},
exportExcel() {
this.isShowLoading(true);
let params = this.options;
params.page = page;
service.get('api/stats/order/detail/export', { params }).then((res) => {
if (res.code === 0) {
this.downloadFile(res.data);
}
this.isShowLoading(false);
}).catch(() => {
this.isShowLoading(false);
});
}
}
};

View File

@ -1,16 +1,28 @@
import { sumBy } from 'service/util';
import {
sumBy
} from 'service/util';
export default {
name: 'StatsOrder',
components: {
UiDetail: resolve => require(['views/stats/order/detail'], resolve)
},
data() {
return {
search: {
show: true
},
detailObj: {
type: null,
options: {}
},
options: {
company_name: null,
package_name: null,
pay_channel: null,
time: null
time: [
this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM-DD'),
this.moment().subtract('2', 'months').endOf('month').format('YYYY-MM-DD')
]
},
data: [],
list: [],
@ -20,43 +32,99 @@ export default {
limit: 10,
page: 1
},
columns: [
{
title: '企业名称',
key: 'company_name'
},
{
title: '套餐名称',
key: 'package_name',
width: 150
},
{
title: '支付方式',
key: 'pay_channel_name',
width: 120
},
{
title: '销售单价',
key: 'unit_price',
width: 120
},
{
title: '销售数量',
key: 'counts',
width: 120
},
{
title: '销售总金额',
key: 'custom_price',
width: 120
columns: [{
title: '企业名称',
key: 'company_name'
},
{
title: '套餐名称',
key: 'package_name',
width: 150
},
{
title: '支付方式',
key: 'pay_channel_name',
width: 120
},
{
title: '单价',
key: 'unit_price',
width: 120
},
{
title: '数量',
key: 'counts',
width: 120
},
{
title: '总金额',
key: 'custom_price',
width: 150
},
{
title: '操作',
key: 'action',
width: 170,
render: (h, {
row,
column,
index
}) => {
let html = [];
html.push(h('Button', {
props: {
type: 'primary',
size: 'small',
disabled: false,
icon: 'ios-create'
},
class: ['btn'],
on: {
click: (event) => {
this.isShowLoading(true);
let params = {
page: 1,
limit: 10,
type: this.type,
order_id: row.order_id,
orderBy: 'id',
sortedBy: 'asc'
};
service.get('api/stats/order/detail', { params }).then(res => {
this.isShowLoading(false);
if (res.code == 0) {
this.detailObj = {
show: true,
options: params,
list: res.data
};
}
}).catch(() => {
this.isShowLoading(false);
});
}
}
}, '查看明细'));
if (html.length) {
return h('div', html);
}
}
}
]
};
},
created() {
this.type = Number(this.$route.params.type);
this.index();
},
mounted() {
window.onresize = () => {
this.tableFooter();
};
},
methods: {
/**
* [index 列表]
@ -64,6 +132,9 @@ export default {
* @return {[type]} [description]
*/
index() {
this.type = Number(this.$route.params.type);
this.data = [];
let options = Object.assign({
orderBy: 'company_id',
sortedBy: 'asc',
@ -77,7 +148,9 @@ export default {
this.isShowLoading(true);
service.get('api/stats/order', { params }).then(res => {
service.get('api/stats/order', {
params
}).then(res => {
this.isShowLoading(false);
if (res.code == 0) {
this.list = res.data;
@ -106,9 +179,22 @@ export default {
resetSearch() {
for (let k in this.options) {
this.options[k] = null;
if (k === 'time') {
this.options[k] = [
this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM-DD'),
this.moment().subtract('2', 'months').endOf('month').format('YYYY-MM-DD')
];
} else {
this.options[k] = null;
}
}
this.page = {
total: 0,
limit: 10,
page: 1
};
this.index();
},
changeLimit(limit) {
@ -124,6 +210,9 @@ export default {
custom_price: sumBy(this.list, 'custom_price')
};
this.tableFooter();
},
tableFooter() {
this.$nextTick(() => {
setTimeout(() => {
let html = $('.ivu-table-header colgroup').html();
@ -131,12 +220,11 @@ export default {
}, 10);
});
},
exportExcel() {
this.isShowLoading(true);
let options = Object.assign({
orderBy: 'id',
orderBy: 'company_id',
sortedBy: 'asc'
},
@ -146,7 +234,7 @@ export default {
this.isShowLoading(true);
service.get('api/stats/company-count/export', {
service.get('api/stats/order/export', {
params
}).then((res) => {
if (res.code === 0) {

View File

@ -495,7 +495,6 @@ export default {
*/
index(page = 1) {
this.params.type = Number(this.$route.params.type);
console.log(this.params);
let data = this.searchDataHandle({}, { page }, this.params);
this.isShowLoading(true);
API.index(data).then(res => {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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-5e8cf68c.38d56961.css rel=prefetch><link href=/js/chunk-00ae0766.158e4a52.js rel=prefetch><link href=/js/chunk-5e8cf68c.013a2f19.js rel=prefetch><link href=/css/app.36043160.css rel=preload as=style><link href=/css/chunk-vendors.3c3b2e85.css rel=preload as=style><link href=/js/app.42f1171d.js rel=preload as=script><link href=/js/chunk-vendors.02a4e5bc.js rel=preload as=script><link href=/css/chunk-vendors.3c3b2e85.css rel=stylesheet><link href=/css/app.36043160.css rel=stylesheet></head><body><noscript><strong>很抱歉如果没有启用JavaScript程序不能正常工作若要继续使用请启用它。</strong></noscript><div id=app></div><script src=/js/chunk-vendors.02a4e5bc.js></script><script src=/js/app.42f1171d.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-e2894d1a.dbcb68d8.css rel=prefetch><link href=/js/chunk-00ae0766.158e4a52.js rel=prefetch><link href=/js/chunk-e2894d1a.455e5d3d.js rel=prefetch><link href=/css/app.36043160.css rel=preload as=style><link href=/css/chunk-vendors.3c3b2e85.css rel=preload as=style><link href=/js/app.4cda9b2e.js rel=preload as=script><link href=/js/chunk-vendors.02a4e5bc.js rel=preload as=script><link href=/css/chunk-vendors.3c3b2e85.css rel=stylesheet><link href=/css/app.36043160.css rel=stylesheet></head><body><noscript><strong>很抱歉如果没有启用JavaScript程序不能正常工作若要继续使用请启用它。</strong></noscript><div id=app></div><script src=/js/chunk-vendors.02a4e5bc.js></script><script src=/js/app.4cda9b2e.js></script></body></html>