客户管理等

This commit is contained in:
邓皓元 2019-01-03 09:06:27 +08:00
parent 006c8e8966
commit b859a811cd
45 changed files with 1120 additions and 194 deletions

View File

@ -1,12 +1,13 @@
<?php
namespace App\Domains\Stats\Exports;
namespace App\Core;
use App\Models\Stats\Export;
use App\Models\Export\Export;
use Illuminate\Support\Carbon;
use Dipper\Excel\Events\AfterStore;
use Dipper\Excel\Concerns\WithTitle;
use Dipper\Excel\Events\AfterExport;
use Dipper\Excel\Concerns\Exportable;
use Dipper\Excel\Concerns\WithEvents;
use Dipper\Excel\Events\BeforeExport;
use Illuminate\Filesystem\Filesystem;
@ -14,10 +15,12 @@ use Dipper\Excel\Events\BeforeWriting;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Storage;
use Dipper\Excel\Concerns\ShouldAutoSize;
use App\Domains\Stats\Repositories\ExportRepository;
use App\Domains\Export\Repositories\ExportRepository;
abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize
{
use Exportable;
public static $classes = [
\App\Domains\Stats\Exports\CompanyCountExport::class => '企业统计',
];

View File

View File

@ -1,10 +1,11 @@
<?php
namespace App\Domains\Stats\Http\Controllers;
namespace App\Domains\Export\Http\Controllers;
use App\Core\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Storage;
use App\Domains\Stats\Services\ExportService;
use App\Domains\Export\Services\ExportService;
class ExportController extends Controller
{

View File

@ -0,0 +1,33 @@
<?php
namespace App\Domains\Export\Providers;
use Illuminate\Support\ServiceProvider;
use App\Domains\Export\Providers\RouteServiceProvider;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
class ExportServiceProvider extends ServiceProvider
{
/**
* 引导启动任何应用程序服务
*
* php artisan make:migration --path=app/Domains/Export/Database/migrations
*
* @return void
*/
public function boot()
{
// $this->loadMigrationsFrom([realpath(__DIR__ . '/../Database/migrations')]);
// $this->app->make(EloquentFactory::class)->load(realpath(__DIR__ . '/../Database/factories'));
// $this->mergeConfigFrom(realpath(__DIR__ . '/../config.php'), 'domain.export');
}
/**
* 注册一个服务提供者
*
* @return void
*/
public function register()
{
$this->app->register(RouteServiceProvider::class);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Domains\Export\Providers;
use Dipper\Foundation\Core\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* Read the routes from the "api.php" and "web.php" files of this Domain
*/
public function boot()
{
$app = $this->app;
$namespace = 'App\Domains\Export\Http\Controllers';
$pathApi = __DIR__.'/../Routes/api.php';
$pathWeb = __DIR__.'/../Routes/web.php';
$this->loadRoutesFiles($app->router, $namespace, $pathApi, $pathWeb);
}
}

View File

@ -1,9 +1,9 @@
<?php
namespace App\Domains\Stats\Repositories;
namespace App\Domains\Export\Repositories;
use App\Core\Repository;
use App\Models\Stats\Export as Model;
use App\Models\Export\Export as Model;
class ExportRepository extends Repository
{
@ -57,6 +57,10 @@ class ExportRepository extends Repository
$this->model = $this->model->whereIn('id', $conditions['id']);
}
if (isset($conditions['tag'])) {
$this->model = $this->model->where('tag', $conditions['tag']);
}
if (isset($conditions['starttime'])) {
$this->model = $this->model->where('created_at', '>=', $conditions['starttime']);
}

View File

@ -0,0 +1,8 @@
<?php
// Prefix: /api/exports
$router->group(['prefix' => 'exports', 'as' => 'exports', 'middleware' => ['adminAuth']], function ($router) {
// 导出记录
$router->get('/', ['as' => 'index', 'uses' => 'ExportController@index']);
$router->post('/destroy', ['as' => 'destroy', 'uses' => 'ExportController@destroy']);
});

View File

@ -0,0 +1,14 @@
<?php
$router->group(['prefix' => 'exports', 'as' => 'exports'], function($router) {
// The controllers live in Domains/Export/Http/Controllers
// $router->get('/', ['as' => 'index', 'uses' => 'ExportController@index']);
/**
* 需要认证的接口
*/
// $router->group(['middleware' => ['userAuth']], function($router) {
// // $router->post('delete', ['as' => 'delete', 'uses' => 'ExportController@delete']);
// });
});

View File

@ -1,12 +1,12 @@
<?php
namespace App\Domains\Stats\Services;
namespace App\Domains\Export\Services;
use App\Dicts;
use App\Core\Service;
use App\Core\AbstractExport;
use Dipper\Excel\Facades\Excel;
use Illuminate\Support\Facades\Storage;
use App\Domains\Stats\Exports\AbstractExport;
use App\Domains\Stats\Repositories\ExportRepository;
use App\Domains\Export\Repositories\ExportRepository;
class ExportService extends Service
{
@ -57,14 +57,23 @@ class ExportService extends Service
}
}
$url = $item->status === 3 ? Storage::disk($item->disk)->url($item->filename) : '';
$conditions = '';
foreach (array_except($item->conditions, ['page', 'limit', 'orderBy', 'sortedBy']) as $key => $value) {
$conditions .= "$key: $value\n";
}
return [
'id' => $item->id,
'tag_name' => $classes[$item['tag']] ?? '未知',
'filesize' => human_filesize($item->filesize),
'dateline' => !$item->conditions['starttime'] ? '所有' : $item->conditions['starttime'] . ' 至 ' . $item->conditions['endtime'],
'conditions' => $conditions,
'status' => $item->status,
'status_name' => $status[$item->status],
'url' => $status[$item->status] === 3 ? Storage::disk($item->disk)->url($item->filename) : '',
'url' => $url,
'created_at' => (string)$item->created_at,
'updated_at' => (string)$item->updated_at,
];

View File

@ -0,0 +1,13 @@
<?php
namespace App\Domains\Export\Tests\Services;
use App\Core\TestCase;
use App\Domains\Export\Services\ExportService;
class ExportServiceTest extends TestCase
{
public function testExportServiceTest()
{
$this->assertTrue(true);
}
}

View File

@ -0,0 +1,11 @@
{
"name": "app/export",
"description": "",
"type": "app-domain",
"require": {
},
"autoload": {
}
}

View File

@ -2,6 +2,7 @@
namespace App\Domains\Stats\Exports;
use App\Core\AbstractExport;
use Dipper\Excel\Concerns\Exportable;
use Dipper\Excel\Concerns\WithHeadings;
use Dipper\Excel\Concerns\FromCollection;
@ -10,8 +11,6 @@ use App\Domains\Stats\Services\CompanyCountService;
class CompanyCountExport extends AbstractExport implements FromCollection, WithHeadings, WithHeadingRow
{
use Exportable;
public $conditions;
public function __construct(array $conditions = [])

View File

@ -5,7 +5,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Dipper\Excel\Facades\Excel;
use Illuminate\Support\Facades\Storage;
use App\Domains\Stats\Services\ExportService;
use App\Domains\Export\Services\ExportService;
use App\Domains\Stats\Exports\CompanyCountExport;
use App\Domains\Stats\Services\CompanyCountService;

View File

@ -2,11 +2,6 @@
// Prefix: /api/stats
$router->group(['prefix' => 'stats', 'as' => 'stats', 'middleware' => ['adminAuth']], function ($router) {
// 导出记录
$router->get('/exports', ['as' => 'exports', 'uses' => 'ExportController@index']);
$router->post('/exports/destroy', ['as' => 'exports.destroy', 'uses' => 'ExportController@destroy']);
// 企业统计
$router->get('/company-count', ['as' => 'company-count.index', 'uses' => 'CompanyCountController@index']);
$router->get('/company-count/export', ['as' => 'company-count.export', 'uses' => 'CompanyCountController@export']);

View File

@ -0,0 +1,63 @@
<?php
namespace App\Domains\Virtual\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\Virtual\Services\CardService;
class CardExport extends AbstractExport implements FromCollection, WithHeadings, WithHeadingRow
{
public $conditions;
public function __construct(array $conditions = [])
{
$this->conditions = $conditions;
parent::__construct();
}
public function collection()
{
set_time_limit(-1);
$collection = app(CardService::class)->index($this->conditions);
$collection = $collection->only([
'id',
'sim',
'imsi',
'iccid',
'carrier_operator',
'company_name',
'package_name',
'virtual_activated_at',
'status_name',
'created_at',
'service_start_at',
'service_end_at',
]);
return $collection;
}
public function headings(): array
{
return [
'客户编号',
'SIM',
'IMSI',
'ICCID',
'运营商',
'企业名称',
'套餐名称',
'激活时间',
'状态',
'创建时间',
'服务开始时间',
'服务结束时间',
];
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace App\Domains\Virtual\Http\Controllers;
use App\Core\Controller;
use Illuminate\Http\Request;
use App\Domains\Virtual\Exports\CardExport;
use App\Domains\Virtual\Services\CardService;
use App\Domains\Export\Services\ExportService;
class CardController extends Controller
{
protected $request;
protected $cardService;
/**
* 构造函数,自动注入.
*/
public function __construct(Request $request, CardService $cardService)
{
$this->request = $request;
$this->cardService = $cardService;
}
/**
* 列表.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$conditions = $this->request->all();
$conditions['limit'] = $this->request->get('limit', 20);
if ($conditions['id']) {
$conditions['id'] = intval(str_replace('No', '', $conditions['id']));
}
$cards = $this->cardService->index($conditions);
return res($cards, '卡列表', 201);
}
/**
* 导出.
*
* @return \Illuminate\Http\Response
*/
public function export()
{
$conditions = $this->request->all();
$conditions['limit'] = 0;
if ($conditions['id']) {
$conditions['id'] = intval(str_replace('No', '', $conditions['id']));
}
$total = $this->orderCardRepository->withConditions($conditions)->applyConditions()->count();
try {
$export = new CardExport($conditions);
$url = ExportService::store($export, $this->disk);
} catch (\Exception $e) {
return err('导出失败,请稍后重试');
}
return res($url, '导出成功', 201);
}
}

View File

@ -4,41 +4,6 @@ namespace App\Domains\Virtual\Repositories\Concerns;
trait OrderCardConcern
{
/**
* 是否关闭缓存
*
* @var boolean
*/
protected $cacheSkip = false;
/**
* 是否开启数据转化
*
* @var bool
*/
protected $needTransform = false;
/**
* @var array
*/
protected $fieldSearchable = [
'id' => '=',
'created_at' => 'like',
];
/**
* 数据格式化
*
* @param mixed $result
*
* @return mixed
*/
public function transform($model)
{
return $model->toArray();
}
/**
* 查询条件
*
@ -51,6 +16,11 @@ trait OrderCardConcern
$this->model = $this->model->whereIn('id', $conditions['id']);
}
if (isset($conditions['sim'])) {
$conditions['sim'] = array_wrap($conditions['sim']);
$this->model = $this->model->whereIn('sim', $conditions['sim']);
}
if (isset($conditions['company_id'])) {
$this->model = $this->model->where('company_id', $conditions['company_id']);
}
@ -82,11 +52,11 @@ trait OrderCardConcern
}
if (isset($conditions['starttime'])) {
$this->model = $this->model->where('order_at', '>=', $conditions['starttime']);
$this->model = $this->model->where('created_at', '>=', $conditions['starttime']);
}
if (isset($conditions['endtime'])) {
$this->model = $this->model->where('order_at', '<=', $conditions['endtime']);
$this->model = $this->model->where('created_at', '<=', $conditions['endtime']);
}
return $this;

View File

@ -10,6 +10,40 @@ class OrderCardRepository extends Repository
{
use OrderCardConcern;
/**
* 是否关闭缓存
*
* @var boolean
*/
protected $cacheSkip = false;
/**
* 是否开启数据转化
*
* @var bool
*/
protected $needTransform = false;
/**
* @var array
*/
protected $fieldSearchable = [
'id' => '=',
'created_at' => 'like',
];
/**
* 数据格式化
*
* @param mixed $result
*
* @return mixed
*/
public function transform($model)
{
return $model->toArray();
}
public function model()
{
return Model::class;

View File

@ -10,6 +10,40 @@ class OrderFlowPackageCardsRepository extends Repository
{
use OrderCardConcern;
/**
* 是否关闭缓存
*
* @var boolean
*/
protected $cacheSkip = false;
/**
* 是否开启数据转化
*
* @var bool
*/
protected $needTransform = false;
/**
* @var array
*/
protected $fieldSearchable = [
'id' => '=',
'created_at' => 'like',
];
/**
* 数据格式化
*
* @param mixed $result
*
* @return mixed
*/
public function transform($model)
{
return $model->toArray();
}
public function model()
{
return Model::class;

View File

@ -10,6 +10,40 @@ class OrderRenewalCardRepository extends Repository
{
use OrderCardConcern;
/**
* 是否关闭缓存
*
* @var boolean
*/
protected $cacheSkip = false;
/**
* 是否开启数据转化
*
* @var bool
*/
protected $needTransform = false;
/**
* @var array
*/
protected $fieldSearchable = [
'id' => '=',
'created_at' => 'like',
];
/**
* 数据格式化
*
* @param mixed $result
*
* @return mixed
*/
public function transform($model)
{
return $model->toArray();
}
public function model()
{
return Model::class;

View File

@ -10,6 +10,40 @@ class OrderRenewalPackageCardRepository extends Repository
{
use OrderCardConcern;
/**
* 是否关闭缓存
*
* @var boolean
*/
protected $cacheSkip = false;
/**
* 是否开启数据转化
*
* @var bool
*/
protected $needTransform = false;
/**
* @var array
*/
protected $fieldSearchable = [
'id' => '=',
'created_at' => 'like',
];
/**
* 数据格式化
*
* @param mixed $result
*
* @return mixed
*/
public function transform($model)
{
return $model->toArray();
}
public function model()
{
return Model::class;

View File

@ -49,13 +49,7 @@ $router->group(['prefix' => 'virtual', 'as' => 'virtual', 'middleware' => ['admi
$router->post('/orders/update/{id}', ['as' => 'orders.update', 'uses' => 'OrderController@update']);
$router->post('/orders/destroy', ['as' => 'orders.destroy', 'uses' => 'OrderController@destroy']);
// 数据统计
$router->get('/stat/company-index', ['as' => 'stat.company-index', 'uses' => 'StatController@companyIndex']);
/**
* 需要认证的接口
*/
// $router->group(['middleware' => ['adminAuth']], function($router) {
// // $router->post('delete', ['as' => 'delete', 'uses' => 'VirtualController@delete']);
// });
// 客户管理
$router->get('/cards/index', ['as' => 'cards.index', 'uses' => 'CardController@index']);
$router->get('/cards/export', ['as' => 'cards.export', 'uses' => 'CardController@export']);
});

View File

@ -0,0 +1,136 @@
<?php
namespace App\Domains\Virtual\Services;
use App\Dicts;
use App\Core\Service;
use App\Models\Card\Card;
use Illuminate\Support\Carbon;
use App\Models\Virtual\OrderCard;
use App\Domains\Card\Repositories\CardRepository;
use App\Domains\Virtual\Repositories\OrderCardRepository;
class CardService extends Service
{
protected $orderCardRepository;
protected $cardRepository;
/**
* 构造函数
*
* @return void
*/
public function __construct(OrderCardRepository $orderCardRepository, CardRepository $cardRepository)
{
$this->orderCardRepository = $orderCardRepository;
$this->cardRepository = $cardRepository;
}
/**
* 卡列表
*
* @param array $conditions
* @return mixed
*/
public function index(array $conditions = [])
{
$limit = $conditions['limit'] ?? 20;
$cards = $this->orderCardRepository->with(['card', 'company:id,name', 'package:id,name,carrier_operator,service_months', 'renewals', 'renewalPackages'])
->withConditions($conditions)->applyConditions()->paginate($limit);
$carrierOperators = app(Dicts::class)->get('carrier_operator');
$cardStatus = app(Dicts::class)->get('card_status');
$cards->transform(function ($item) use ($carrierOperators, $cardStatus) {
$status = $this->getStatus($item->card);
$timelines = $this->timelines($item);
$service_start_at = min(array_pluck($timelines, 'starttime'));
$service_end_at = max(array_pluck($timelines, 'endtime'));
return [
'id' => sprintf('No%011d', $item->id),
'sim' => $item->sim,
'imsi' => $item->card['imsi'],
'iccid' => $item->card['iccid'],
'carrier_operator' => $carrierOperators[$item->card['carrier_operator']],
'company_name' => $item->company['name'],
'package_name' => $item->package['name'],
'virtual_activated_at' => (string)$item->card['virtual_activated_at'],
'status' => $status,
'status_name' => $cardStatus[$status],
'created_at' => (string)$item->created_at,
'updated_at' => (string)$item->updated_at,
'service_start_at' => $service_start_at,
'service_end_at' => $service_end_at,
'timelines' => $timelines,
];
});
return $cards;
}
/**
* 卡状态
*
* @param Card $card
* @return void
*/
public static function getStatus($card)
{
return $card['virtual_activated_at'] ? ($card['cancelled_at'] ? 3 : 2) : 1;
}
/**
* 生命周期
* @param OrderCard $item
*/
public static function timelines($item)
{
$array = [];
$virtual_activated_at = $item->card['virtual_activated_at'] ? Carbon::parse($item->card['virtual_activated_at'])->format('Y-m') : '';
// 基础套餐
$array[] = [
'type' => 0,
'name' => $item->package['name'],
'starttime' => $item->card['virtual_activated_at'] ? Carbon::parse($item->card['virtual_activated_at'])->format('Y-m') : '',
'endtime' => $virtual_activated_at ? Carbon::parse($virtual_activated_at)->addMonths($item->package['service_months'])->format('Y-m') : '',
];
// 续费套餐
$packages = $item->renewals->merge($item->renewalPackages);
$packages = $packages->sortBy(function ($value) {
return $value->pivot->created_at;
});
$packages->map(function ($package) use ($next, $virtual_activated_at, &$array) {
if (!$virtual_activated_at) {
$starttime = $endtime = '';
} else {
$starttime = (Carbon::parse($package->pivot->created_at) < Carbon::parse($next)) ? Carbon::parse($next) : Carbon::parse($package->pivot->created_at);
$endtime = $starttime->copy()->addMonths($package['service_months'] * $package->pivot->counts);
$next = $endtime;
$starttime = $starttime->format('Y-m');
$endtime = $endtime->format('Y-m');
}
$array[] = [
'type' => $package->type + 1,
'name' => $package->name,
'starttime' => $starttime,
'endtime' => $endtime,
];
});
$typeNames = ['基础套餐', '基础续费', '续费包'];
foreach ($array as $key => &$value) {
$value['type_name'] = $typeNames[$value['type']];
}
return $array;
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Domains\Virtual\Tests\Services;
use App\Core\TestCase;
use App\Domains\Virtual\Services\CardService;
class CardServiceTest extends TestCase
{
public function testCardServiceTest()
{
$this->assertTrue(true);
}
}

View File

@ -1,12 +1,12 @@
<?php
namespace App\Models\Stats;
namespace App\Models\Export;
use App\Core\Model;
class Export extends Model
{
protected $table = 'stats_exports';
protected $table = 'exports';
protected $fillable = ['id', 'sn', 'tag', 'filename' , 'filesize', 'conditions', 'status', 'progress'];

View File

@ -15,16 +15,16 @@ class OrderCard extends Model
public function renewals()
{
return $this->belongsToMany(Package::class, 'virtual_order_renewal_cards', 'sim', 'package_id', 'sim', 'id');
return $this->belongsToMany(Package::class, 'virtual_order_renewal_cards', 'sim', 'package_id', 'sim', 'id')->withPivot('created_at', 'counts');
}
public function renewalPackages()
{
return $this->belongsToMany(Package::class, 'virtual_order_renewal_package_cards', 'sim', 'package_id', 'sim', 'id');
return $this->belongsToMany(Package::class, 'virtual_order_renewal_package_cards', 'sim', 'package_id', 'sim', 'id')->withPivot('created_at', 'counts');
}
public function flowPackages()
{
return $this->belongsToMany(Package::class, 'virtual_order_flows_package_cards', 'sim', 'package_id', 'sim', 'id');
return $this->belongsToMany(Package::class, 'virtual_order_flows_package_cards', 'sim', 'package_id', 'sim', 'id')->withPivot('created_at', 'counts');
}
}

View File

@ -13,7 +13,7 @@ class CreateStatsExportsTable extends Migration
*/
public function up()
{
Schema::create('stats_exports', function (Blueprint $table) {
Schema::create('exports', function (Blueprint $table) {
$table->increments('id')->comment('自增ID');
$table->string('sn', 32)->default('')->comment('命令编号');
$table->string('tag', 20)->default('')->comment('标记');
@ -37,6 +37,6 @@ class CreateStatsExportsTable extends Migration
*/
public function down()
{
Schema::dropIfExists('stats_exports');
Schema::dropIfExists('exports');
}
}

View File

@ -110,6 +110,22 @@ class PermissionSeeder extends Seeder
],
],
],
[
'name' => 'virtual_card_ctrl',
'title' => '客户管理',
'path' => '#',
'icon' => 'ios-settings',
'type' => 0,
'open' => 3,
'children' => [
[
'name' => 'virtual.cards.index', 'title' => '客户列表', 'path' => '/cards', 'icon' => 'ios-cube', 'type' => 0, 'open' => 3,
'children' => [
['name' => 'virtual.cards.show', 'title' => '查看', 'description' => 'show', 'type' => 1],
],
],
],
],
[
'name' => 'virtual_order_ctrl',
'title' => '订单管理',
@ -137,17 +153,22 @@ class PermissionSeeder extends Seeder
'type' => 0,
'open' => 3,
'children' => [
[
'name' => 'stat.exports.index', 'title' => '导出记录', 'path' => '/stats/exports', 'icon' => 'ios-download', 'type' => 0, 'open' => 3,
'children' => [
['name' => 'stat.exports.destroy', 'title' => '删除', 'description' => 'destroy', 'type' => 1],
],
],
[
'name' => 'stats.company-index', 'title' => '企业统计', 'path' => '/stats/company-count', 'icon' => 'md-pulse', 'type' => 0, 'open' => 3
],
],
],
[
'name' => 'export.index',
'title' => '导出记录',
'path' => '/exports',
'icon' => 'ios-download',
'type' => 0,
'open' => 3,
'children' => [
['name' => 'exports.destroy', 'title' => '删除', 'description' => 'destroy', 'type' => 1],
],
],
];
/**

View File

@ -0,0 +1,25 @@
/**
* 客户管理
*/
/**
* [index 客户列表]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
export function index(data) {
return service.get('api/virtual/cards/index', {
params: data
});
}
/**
* [show 客户详情]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
export function exportExcel(data) {
return service.get('api/virtual/cards/export', {
params: data
});
}

View File

@ -124,7 +124,7 @@ export default {
parseTime(time) {
return {
starttime: this.moment(time[0]).format('YYYY-MM-DD') + ' 00:00:00',
endtime: this.moment(time[1]).format('YYYY-MM-DD') + ' 00:00:00'
endtime: this.moment(time[1]).format('YYYY-MM-DD') + ' 23:59:59'
};
},

View File

@ -22,7 +22,8 @@ const routes = [
{ path: '/products', name: 'Products', component: load('virtual/products/index'), meta: { title: '定价管理' } },
{ path: '/orders', name: 'Orders', component: load('virtual/orders/index'), meta: { title: '订单列表' } },
{ path: '/packages', name: 'Packages', component: load('virtual/packages/index'), meta: { title: '套餐管理' } },
{ path: '/stats/exports', name: 'StatsExports', component: load('stats/exports/index'), meta: { title: '导出记录' } },
{ 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: '企业统计' } }
]
},

View File

@ -0,0 +1,70 @@
<template>
<div class="page-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="destroyBatch" icon="md-trash" type="primary" v-has="'destroy'">删除</Button>
</div>
<div class="handle-item">
<Button @click="search.show=!search.show" ghost icon="ios-search" type="primary">搜索</Button>
</div>
<div class="handle-item">
<Button @click="index(1)" icon="md-refresh">刷新</Button>
</div>
</li>
</ul>
<div class="search-wrap" v-show="search.show">
<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>
</Select>
</li>
<li class="handle-item w-350">
<DatePicker :editable="false" placeholder="请选择时间" placement="bottom-start" type="daterange" v-model.trim="other.time"></DatePicker>
</li>
</ul>
<ul class="handle-wraper">
<li class="f-r">
<div class="handle-item">
<Button @click="index(1)" 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">
<Alert class="page-tips" show-icon>
已选
<span class="num">{{selection.length}}</span>
<span @click="handleSelectAll(false)" class="clear">清空</span>
</Alert>
<Table :columns="table_titles" :data="list_data?list_data.data:[]" @on-selection-change="selectionChange" ref="table"></Table>
</div>
<div class="page-turn-wrap" v-if="list_data && list_data.data.length">
<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>
</template>
<script src="./js/index.js"></script>

View File

@ -6,7 +6,8 @@ export default {
request_param: ''
},
other: {
time: []
time: [],
tag: null
},
list_data: null,
search: {
@ -33,9 +34,12 @@ export default {
width: 120
},
{
title: '查询范围',
key: 'dateline',
width: 300
title: '查询条件',
key: '',
width: 300,
render: (h, { row, column, index }) => {
return h('pre', row.conditions);
}
},
{
title: '文件大小',
@ -111,7 +115,7 @@ export default {
let data = this.searchDataHandle(this.params, { page }, this.other);
this.isShowLoading(true);
console.log(data);
service.get('api/stats/exports', { params: data }).then(res => {
service.get('api/exports', { params: data }).then(res => {
this.isShowLoading(false);
if (res.code == 0) {
let result = res.data;
@ -156,7 +160,7 @@ export default {
title: '提示',
content: '确认执行删除操作?',
onOk: () => {
service.post('api/stats/exports/destroy', data).then(res => {
service.post('api/exports/destroy', data).then(res => {
if (res.code == 0) {
// 当有勾选项删除操作的地方为每行的按钮将复选框勾选项去除此id
const ids = data.ids.toString().split(',');
@ -195,6 +199,7 @@ export default {
for (let k in this.params) {
this.params[k] = '';
}
this.other.tag = null;
this.other.time = [];
this.index(1);
},

View File

@ -1,41 +1,41 @@
<template>
<div>
<div class="header-bar">
<div class="collapsed-wrap">
<a @click="collapsedChange" type="text">
<Icon class="shrink" type="md-menu" size="26" :class="{'collapsed':collapsed}" />
</a>
</div>
<div class="head-other">
<slot></slot>
</div>
<div class="user-wrap">
<Dropdown trigger="click" :transfer="true" @on-click="dropChange">
<a href="javascript:void(0)" class="user-name" v-if="account">
<template>
<span>
{{account.username}}
<Icon type="md-arrow-dropdown" size="17" />
</span>
</template>
<img class="head-img" :src="account.avatar" @error="imgError($event,default_head)">
</a>
<DropdownMenu slot="list">
<DropdownItem :name="1">修改密码</DropdownItem>
<DropdownItem :name="2">个人信息</DropdownItem>
<DropdownItem :name="3" divided>安全退出</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
<div>
<div class="header-bar">
<div class="collapsed-wrap">
<a @click="collapsedChange" type="text">
<Icon :class="{'collapsed':collapsed}" class="shrink" size="26" type="md-menu"/>
</a>
</div>
<ui-psw :show.sync="password.show"></ui-psw>
<div class="head-other">
<slot></slot>
</div>
<ui-detail :show.sync="detail.show"></ui-detail>
</div>
<div class="user-wrap">
<Dropdown :transfer="true" @on-click="dropChange" trigger="click">
<a class="user-name" href="javascript:void(0)" v-if="account">
<template>
<span>
{{account.username}}
<Icon size="17" type="md-arrow-dropdown"/>
</span>
</template>
<img :src="account.avatar" @error="imgError($event,default_head)" class="head-img">
</a>
<DropdownMenu slot="list">
<DropdownItem :name="1">修改密码</DropdownItem>
<DropdownItem :name="2">个人信息</DropdownItem>
<DropdownItem :name="3" divided>安全退出</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</div>
<ui-psw :show.sync="password.show"></ui-psw>
<ui-detail :show.sync="detail.show"></ui-detail>
</div>
</template>
<script src="./js/header_bar.js"></script>

View File

@ -1,73 +0,0 @@
<template>
<div class="page-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 v-has="'destroy'" type="primary" icon="md-trash" @click="destroyBatch">删除</Button>
</div>
<div class="handle-item">
<Button type="primary" icon="ios-search" ghost @click="search.show=!search.show">搜索</Button>
</div>
<div class="handle-item">
<Button icon="md-refresh" @click="index(1)">刷新</Button>
</div>
</li>
</ul>
<div class="search-wrap" v-show="search.show">
<ul class="handle-wraper">
<li class="handle-item w-350">
<DatePicker
:editable="false"
type="daterange"
placement="bottom-start"
placeholder="请选择时间"
v-model.trim="other.time">
</DatePicker>
</li>
</ul>
<ul class="handle-wraper">
<li class="f-r">
<div class="handle-item">
<Button type="primary" ghost @click="index(1)">立即搜索</Button>
</div>
<div class="handle-item">
<Button type="warning" ghost @click="resetSearch">重置搜索</Button>
</div>
</li>
</ul>
</div>
</div>
<div class="page-list-wrap">
<Alert show-icon class="page-tips">
已选<span class="num">{{selection.length}}</span>
<span class="clear" @click="handleSelectAll(false)">清空</span>
</Alert>
<Table ref="table" :columns="table_titles" :data="list_data?list_data.data:[]" @on-selection-change="selectionChange"></Table>
</div>
<div class="page-turn-wrap" v-if="list_data && list_data.data.length">
<Page
show-total
show-elevator
:current="Number(list_data.current_page)"
:total="Number(list_data.total)"
:page-size="Number(list_data.per_page)"
@on-change="index">
</Page>
</div>
</div>
</template>
<script src="./js/index.js"></script>

View File

@ -0,0 +1,67 @@
<template>
<Drawer :mask-closable="true" @on-visible-change="visibleChange" title="客户详情" v-model="my_show" width="500">
<div class="page-detail-wrap" v-if="data">
<Divider>基础资料</Divider>
<ul>
<li class="ui-list">
<div class="ui-list-title">客户编号:</div>
<div class="ui-list-content">{{data.id}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">SIM:</div>
<div class="ui-list-content">{{data.sim}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">IMSI:</div>
<div class="ui-list-content">{{data.imsi}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">ICCID:</div>
<div class="ui-list-content">{{data.iccid}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">运营商:</div>
<div class="ui-list-content">{{data.carrier_operator}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">所属企业:</div>
<div class="ui-list-content">{{data.company_name}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">卡状态:</div>
<div class="ui-list-content">{{data.status_name}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">创建时间:</div>
<div class="ui-list-content">{{data.created_at}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">更新时间:</div>
<div class="ui-list-content">{{data.updated_at}}</div>
</li>
</ul>
<Divider>服务时间段</Divider>
<ul v-for="item in data.timelines">
<li class="ui-list">
<Row>
<Col span="8">{{item.name}}</Col>
<Col span="8">{{item.type_name}}</Col>
<Col span="8">{{item.starttime}} {{item.endtime}}</Col>
</Row>
</li>
</ul>
</div>
</Drawer>
</template>
<script src="./js/detail.js"></script>

View File

@ -0,0 +1,90 @@
<template>
<div class="page-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="openEdit(true, null)" icon="md-add" type="primary" v-has="'create'">添加企业</Button>
</div>-->
<div class="handle-item">
<Button @click="search.show=!search.show" ghost icon="ios-search" type="primary">搜索</Button>
</div>
<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="search-wrap" v-show="search.show">
<ul class="handle-wraper">
<li class="handle-item w-250">
<AutoComplete @on-search="handleCompleteCompanies" icon="ios-search" placeholder="企业名称" v-model.trim="params.company_name">
<Option :key="item.id" :value="item.name" v-for="item in completeHandledCompanies">{{ item.name }}</Option>
</AutoComplete>
</li>
<li class="handle-item w-250">
<AutoComplete @on-search="handleCompletePackages" icon="ios-search" placeholder="套餐名称" v-model.trim="params.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">
<DatePicker :editable="false" placeholder="创建时间" placement="bottom-start" type="daterange" v-model.trim="params.time"></DatePicker>
</li>
</ul>
<ul class="handle-wraper">
<li class="handle-item w-250">
<Select clearable placeholder="运营商" v-model="params.carrier_operator">
<Option :value="0">联通</Option>
<Option :value="1">移动</Option>
<Option :value="2">电信</Option>
</Select>
</li>
<li class="handle-item w-250">
<Input clearable placeholder="客户编号" v-model.trim="params.id"></Input>
</li>
<li class="handle-item w-250">
<Input placeholder="SIM" type="textarea" v-model="params.sim"/>
</li>
<li class="f-r">
<div class="handle-item">
<Button @click="index(1)" 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="table_titles" :data="list_data ? list_data.data : []"></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>
<ui-detail :data="detailObj.data" :show.sync="detailObj.show"></ui-detail>
</div>
</template>
<script src="./js/index.js"></script>

View File

@ -0,0 +1,29 @@
export default{
props: {
show: {
type: Boolean,
default: false
},
data: {
type: Object,
default() {
return null;
}
}
},
watch: {
show(bool) {
this.my_show = bool;
}
},
data() {
return {
my_show: false
};
},
methods: {
visibleChange(bool) {
this.$emit('update:show', bool);
}
}
};

View File

@ -0,0 +1,193 @@
import * as API from 'api/virtual/cards';
export default {
name: 'Cards',
components: {
UiDetail: resolve => require(['views/virtual/cards/detail'], resolve)
},
data() {
return {
params: {
'id': null,
'sim': null,
'company_name': null,
'package_name': null,
'carrier_operator': null,
'time': []
},
list_data: null,
detailObj: {
show: false,
data: null
},
search: {
show: false
},
table_titles: [
{
title: '客户编号',
key: 'id',
width: 150
},
{
title: 'SIM',
key: 'sim',
width: 150
},
{
title: '运营商',
key: 'carrier_operator',
width: 90
},
{
title: '企业名称',
key: 'company_name',
width: 300
},
{
title: '套餐名称',
key: 'package_name',
width: 110
},
{
title: '状态',
key: 'status_name',
width: 110
},
{
title: '服务时间',
key: '',
width: 170,
render: (h, { row, column, index }) => {
return h('span', row.service_start_at + ' - ' + row.service_end_at);
}
},
{
title: '创建时间',
key: 'created_at',
width: 170
},
{
title: '操作',
key: 'action',
render: (h, {
row,
column,
index
}) => {
let html = [];
if (this.haveJurisdiction('show')) {
html.push(h('Button', {
props: {
type: 'dashed',
size: 'small',
disabled: false,
icon: 'md-eye'
},
class: ['btn'],
on: {
click: (event) => {
this.detailObj = {
show: true,
data: row
};
}
}
}, '查看'));
}
if (html.length) {
return h('div', html);
}
}
}
]
};
},
created() {
this.index(1);
},
methods: {
/**
* [index 列表]
* @param {Number} page [description]
* @return {[type]} [description]
*/
index(page = 1) {
let params = Object.assign({
orderBy: 'id',
sortedBy: 'asc'
}, this.params);
if (this.params.sim) {
params.sim = this.params.sim.split(/[\s|,|;]+/);
}
let data = this.searchDataHandle({}, { page }, params);
this.isShowLoading(true);
API.index(data).then(res => {
this.isShowLoading(false);
if (res.code == 0) {
this.list_data = res.data;
}
}).catch(() => {
this.isShowLoading(false);
});
},
/**
* [request 刷新]
* @return {[type]} [description]
*/
request() {
const result = this.list_data;
let page = result.current_page;
if (result && result.data.length == 1) {
page = this.returnPage(result.total, result.current_page, result.per_page);
}
this.index(page);
},
resetSearch() {
for (let k in this.params) {
if (k === 'time') {
this.params[k] = [];
} else {
this.params[k] = null;
}
}
this.index(1);
},
exportExcel() {
let params = Object.assign({
orderBy: 'id',
sortedBy: 'asc'
}, this.params);
if (this.params.sim) {
params.sim = this.params.sim.split(/[\s|,|;]+/);
}
let data = this.searchDataHandle({}, { limit: 0 }, params);
this.isShowLoading(true);
API.exportExcel(data).then(res => {
this.isShowLoading(false);
if (res.url) {
this.downloadFile(res.url);
} else {
this.$Modal.success({
title: '提示',
content: '当前导出数据量大,已进入后台队列导出模式,请稍后至导出列表查看下载。'
});
}
}).catch(() => {
this.isShowLoading(false);
});
}
}
};

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-a0b8384a.2b7901ac.css rel=prefetch><link href=/js/chunk-00ae0766.16700330.js rel=prefetch><link href=/js/chunk-a0b8384a.d3cdc6cf.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.6330bcc5.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.6330bcc5.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-7b30bdf4.a0bc7fe7.css rel=prefetch><link href=/js/chunk-00ae0766.16700330.js rel=prefetch><link href=/js/chunk-7b30bdf4.362c903a.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.42556444.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.42556444.js></script></body></html>