diff --git a/application/common/model/OrderDispatch.php b/application/common/model/OrderDispatch.php
index 4de16f0..7ae55f7 100644
--- a/application/common/model/OrderDispatch.php
+++ b/application/common/model/OrderDispatch.php
@@ -21,6 +21,10 @@ class OrderDispatch extends Model
public function getArriveImagesAttr($val)
{
+ if (empty($val)) {
+ return [];
+ }
+
$images = explode(',', $val);
foreach ($images as $k => $v) {
$images[$k] = cdnurl($v, true);
@@ -31,6 +35,10 @@ class OrderDispatch extends Model
public function getImagesAttr($val)
{
+ if (empty($val)) {
+ return [];
+ }
+
$images = explode(',', $val);
foreach ($images as $k => $v) {
$images[$k] = cdnurl($v, true);
@@ -41,6 +49,10 @@ class OrderDispatch extends Model
public function getImageAttr($val)
{
+ if (empty($val)) {
+ return '';
+ }
+
return cdnurl($val, true);
}
}
diff --git a/application/config.php b/application/config.php
index a92b1f1..826251f 100755
--- a/application/config.php
+++ b/application/config.php
@@ -218,7 +218,7 @@ return [
'host' => Env::get('redis.redis_host'), // Redis 服务器地址
'port' => Env::get('redis.redis_port'), // Redis 端口
'password' => Env::get('redis.redis_password'), // Redis 密码,如果有的话
- 'select' => Env::get('redis.redis_db'),
+ 'select' => 5,
],
// +----------------------------------------------------------------------
// | Cookie设置
diff --git a/application/services/WorkerService.php b/application/services/WorkerService.php
index a98feb5..b053f81 100644
--- a/application/services/WorkerService.php
+++ b/application/services/WorkerService.php
@@ -3,9 +3,7 @@
namespace app\services;
use app\common\library\Token;
-use EasyWeChat\Factory;
-use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
-use EasyWeChat\MiniProgram\Application;
+use EasyWeChat\MiniApp\Application;
class WorkerService extends BaseService
{
@@ -47,8 +45,9 @@ class WorkerService extends BaseService
{
$app = $this->getMiniProgramApp();
try {
- $info = $app->auth->session($code);
- } catch (InvalidConfigException $e) {
+ $utils = $app->getUtils();
+ $info = $utils->codeToSession($code);
+ } catch (\Exception $e) {
$this->apiError('登录失败', $e);
}
@@ -82,18 +81,31 @@ class WorkerService extends BaseService
'app_id' => config('mini_program.app_id'),
'secret' => config('mini_program.secret'),
];
- return Factory::miniProgram($config);
+ return new Application($config);
}
/**
- * 解密微信手机号
+ * 通过 code 获取用户手机号
+ * @param string $code
+ * @return array
+ */
+ private function code2PhoneNumberInfo(string $code): array
+ {
+ $app = $this->getMiniProgramApp();
+ $phoneInfo = $app->getClient()->postJson('wxa/business/getuserphonenumber', ['code' => $code]);
+
+ return $phoneInfo->toArray();
+ }
+
+ /**
+ * 获取用户手机号
* @param string $code
* @return mixed
*/
public function getPhoneNumber(string $code)
{
- //getPhoneNumber 方法通过魔术方法 __call 获取
- $phoneInfo = $this->getMiniProgramApp()->getPhoneNumber($code);
+ //通过 code 获取手机号信息
+ $phoneInfo = $this->code2PhoneNumberInfo($code);
if (empty($phoneInfo)) {
$this->apiError('获取手机号失败', 0, $phoneInfo);
@@ -137,4 +149,40 @@ class WorkerService extends BaseService
return true;
}
+
+ /**
+ * 更新师傅位置
+ * @param int $workerId
+ * @param float $latitude
+ * @param float $longitude
+ * @return true
+ */
+ public function updateWorkerLocation(int $workerId, float $latitude, float $longitude)
+ {
+ $worker = $this->getWorkerModel()->find($workerId);
+ if (empty($worker)) {
+ $this->apiError('更新位置失败,用户不存在');
+ }
+
+ $worker->lat = $latitude;
+ $worker->lng = $longitude;
+ $worker->location_update_time = datetime(time());
+ $worker->update_time = datetime(time());
+ $worker->save();
+
+ return true;
+ }
}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/application/worker/controller/Worker.php b/application/worker/controller/Worker.php
index 38eecc0..5b42d65 100644
--- a/application/worker/controller/Worker.php
+++ b/application/worker/controller/Worker.php
@@ -77,4 +77,21 @@ class Worker extends WorkerApi
{
$this->success('操作成功', $this->user);
}
+
+ /**
+ * 更新师傅位置
+ * @return void
+ */
+ public function updateWorkerLocation()
+ {
+ $params = $this->request->request();
+ $validate = $this->validate($params, \app\worker\validate\Worker::class . '.updateWorkerLocation');
+ if ($validate !== true) {
+ $this->error($validate);
+ }
+
+ $this->getWorkerService()->updateWorkerLocation($this->user['id'], $params['latitude'], $params['longitude']);
+
+ $this->success('操作成功');
+ }
}
diff --git a/application/worker/validate/Worker.php b/application/worker/validate/Worker.php
index 43c1b06..db2a52b 100644
--- a/application/worker/validate/Worker.php
+++ b/application/worker/validate/Worker.php
@@ -9,6 +9,8 @@ class Worker extends Validate
protected $rule = [
'code' => 'require|max:128',
'vendor_token' => 'require|max:128',
+ 'latitude' => 'require|number',
+ 'longitude' => 'require|number',
];
protected $message = [
@@ -18,5 +20,6 @@ class Worker extends Validate
protected $scene = [
'login' => ['code'],
'bindPhoneNumber' => ['code', 'vendor_token'],
+ 'updateWorkerLocation' => ['latitude', 'longitude'],
];
}
diff --git a/public/assets/js/backend/orders/dispatchlog.js b/public/assets/js/backend/orders/dispatchlog.js
index bd77d03..94902f6 100644
--- a/public/assets/js/backend/orders/dispatchlog.js
+++ b/public/assets/js/backend/orders/dispatchlog.js
@@ -84,7 +84,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
{field: 'id', title: __('Id')},
//{field: 'dispatch_id', title: __('Dispatch_id')},
//{field: 'worker_id', title: __('Worker_id')},
- {field: 'remark', title: __('跟进内容'), operate: 'LIKE', table: table2, class: 'autocontent', formatter: Table.api.formatter.content},
+ {field: 'remark', title: __('跟进内容'), operate: false, table: table1, class: 'autocontent', formatter: Table.api.formatter.content},
{field: 'images', title: __('跟进依据'), operate: false, events: Table.api.events.image, formatter: Table.api.formatter.images},
/* {field: 'need_notice', title: __('需要提醒'), searchList: {"0":__('否'),"1":__('是')}, formatter: Table.api.formatter.normal},
{field: 'notice_time', title: __('提醒时间'), operate:'RANGE', addclass:'datetimerange', autocomplete:false},*/
@@ -121,7 +121,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
//{field: 'worker_id', title: __('Worker_id')},
// {field: 'status', title: __('Status')},
{field: 'status_text', title: __('Status_text'), operate: 'LIKE'},
- {field: 'remark', title: __('Remark'), operate: 'LIKE', table: table1, class: 'autocontent', formatter: Table.api.formatter.content},
+ {field: 'remark', title: __('Remark'), operate: false, table: table2, class: 'autocontent', formatter: Table.api.formatter.content},
{field: 'create_time', title: __('Create_time'), operate:'RANGE', addclass:'datetimerange', autocomplete:false},
// {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
]
diff --git a/public/assets/js/backend/statistics/dispatcher.js b/public/assets/js/backend/statistics/dispatcher.js
index 572fb87..7af3b11 100644
--- a/public/assets/js/backend/statistics/dispatcher.js
+++ b/public/assets/js/backend/statistics/dispatcher.js
@@ -1,88 +1,185 @@
-define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-theme', 'template','addtabs','moment'], function ($, undefined, Backend, Table, Form,Echarts,undefined,Template,Datatable,Moment) {
var Controller = {
+
index: function () {
- // 初始化表格参数配置
- Table.api.init({
- extend: {
- index_url: 'statistics/dispatcher/index' + location.search,
- add_url: 'statistics/dispatcher/add',
- edit_url: 'statistics/dispatcher/edit',
- del_url: 'statistics/dispatcher/del',
- multi_url: 'statistics/dispatcher/multi',
- import_url: 'statistics/dispatcher/import',
- table: 'order',
+ //绑定事件
+ $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+ var $targetPanel = $($(this).attr("href"));
+ var tabVal = $(this).data('val');
+
+ if (tabVal === 'second') {
+ // 当切换到“统计列表”时,自动刷新表格
+ //$targetPanel.find(".btn-refresh").trigger("click");
+ // 初始化表格参数配置
+ Table.api.init();
+ // 表格2
+ var table2 = $("#table2");
+ table2.bootstrapTable({
+ url: 'statistics/dispatcher/index' + location.search,
+ toolbar: '#toolbar1',
+ sortName: 'id',
+ search: false,
+ commonSearch:true,
+ visible: false,
+ showToggle: false,
+ showColumns: false,
+ showExport: true,
+ searchFormVisible:true,
+ columns: [
+ [
+ //{field: 'id', title: __('Id')},
+ {field: 'id', title: __('ID'),visible:false,operate: false},
+ {field: 'admin_user', title: __('派单员'),operate: "LIKE"},
+ {field: 'count_num', title: __('总订单数'),operate: false},
+ {field: 'finish_num', title: __('完单数'),operate: false},
+ {field: 'total', title: __('成效额(¥)'), operate: false},
+ {field: 'performance', title: __('总业绩(¥)'), operate: false},
+ {field: 'cost_total', title: __('总成本(¥)'), operate: false},
+ {field: 'refund_total', title: __('退款金额(¥)'), operate: false},
+ {field: 'refund_count', title: __('退款单数'), operate: false},
+
+ {field: 'performance_rate', title: __('利润率(%)'), operate: false},
+ {field: 'trans_rate', title: __('转化率(%)'), operate: false},
+ {field: 'cash_value', title: __('变现值'), operate: false},
+ {field: 'performance_avg', title: __('客单利润(¥)'), operate: false},
+ {field: 'total_avg', title: __('客单价(¥)'), operate: false},
+
+ {field: 'avg_time_diff', title: __('派单时效(小时)'), operate: false},
+
+ //{field: 'admin_user', title: __('派单员'),operate: "LIKE",visible:false},
+ //{field: 'city_name', title: __('城市'),operate: "LIKE",visible:false},
+ //{field: 'city_name', title: __('城市'),operate: "LIKE",visible:false},
+ {field: 'time_by', title: __('时间维度'), visible:false,searchList: {"1":__('录单时间'),"2":__('派单时间')},defaultValue:1, formatter: Table.api.formatter.normal},
+ {field: 'daterange', title: __('时间筛选'), addclass:'datetimerange',
+ autocomplete:false,
+ operate: "RANGE",
+ datetimeFormat: "YYYY-MM-DD",
+ //defaultValue:today()+' - '+today(),
+ data:'autocomplete="off" data-local={"format":"YYYY-MM-DD"}',
+ visible:false},
+
+ // {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+ ]
+ ]
+ });
+ // 为表格2绑定事件
+ Table.api.bindevent(table2);
}
+
});
- var table = $("#table");
- // 初始化表格
- table.bootstrapTable({
- url: $.fn.bootstrapTable.defaults.extend.index_url,
- pk: 'id',
- sortName: 'id',
- fixedColumns: true,
- fixedRightNumber: 1,
- columns: [
- [
- {checkbox: true},
- {field: 'id', title: __('Id')},
- {field: 'order_no', title: __('Order_no'), operate: 'LIKE'},
- {field: 'customer', title: __('Customer'), operate: 'LIKE'},
- {field: 'tel', title: __('Tel'), operate: 'LIKE'},
- {field: 'status', title: __('Status'), searchList: {"0":__('Status 0'),"10":__('Status 10'),"20":__('Status 20'),"30":__('Status 30'),"40":__('Status 40'),"50":__('Status 50'),"60":__('Status 60'),"-10":__('Status -10')}, formatter: Table.api.formatter.status},
- {field: 'area_id', title: __('Area_id'), operate: 'LIKE'},
- {field: 'address', title: __('Address'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
- {field: 'lng', title: __('Lng'), operate:'BETWEEN'},
- {field: 'lat', title: __('Lat'), operate:'BETWEEN'},
- {field: 'work_tel_id', title: __('Work_tel_id')},
- {field: 'source_shop', title: __('Source_shop'), operate: 'LIKE'},
- {field: 'source', title: __('Source')},
- {field: 'source_uid', title: __('Source_uid'), operate: 'LIKE'},
- {field: 'item_id', title: __('Item_id')},
- {field: 'item_title', title: __('Item_title'), operate: 'LIKE'},
- {field: 'detail', title: __('Detail'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
- {field: 'remark', title: __('Remark'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
- {field: 'images', title: __('Images'), operate: false, events: Table.api.events.image, formatter: Table.api.formatter.images},
- {field: 'plan_time', title: __('Plan_time'), operate:'RANGE', addclass:'datetimerange', autocomplete:false},
- {field: 'admin_id', title: __('Admin_id')},
- {field: 'coupon_id', title: __('Coupon_id')},
- {field: 'total', title: __('Total'), operate:'BETWEEN'},
- {field: 'online_amount', title: __('Online_amount'), operate:'BETWEEN'},
- {field: 'offline_amount', title: __('Offline_amount'), operate:'BETWEEN'},
- {field: 'online_amount_last', title: __('Online_amount_last'), operate:'BETWEEN'},
- {field: 'offline_amount_type', title: __('Offline_amount_type'), searchList: {"1":__('Offline_amount_type 1'),"2":__('Offline_amount_type 2')}, formatter: Table.api.formatter.normal},
- {field: 'discount_amount', title: __('Discount_amount'), operate:'BETWEEN'},
- {field: 'refund_amount', title: __('Refund_amount'), operate:'BETWEEN'},
- {field: 'worker_refund_amount', title: __('Worker_refund_amount'), operate:'BETWEEN'},
- {field: 'real_amount', title: __('Real_amount'), operate:'BETWEEN'},
- {field: 'cost', title: __('Cost'), operate:'BETWEEN'},
- {field: 'material_cost', title: __('Material_cost'), operate:'BETWEEN'},
- {field: 'performance', title: __('Performance'), operate:'BETWEEN'},
- {field: 'cancel_reason_id', title: __('Cancel_reason_id')},
- {field: 'cancel_detail', title: __('Cancel_detail'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
- {field: 'audit_remark', title: __('Audit_remark'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content},
- {field: 'audit_admin_id', title: __('Audit_admin_id')},
- {field: 'create_time', title: __('Create_time'), operate:'RANGE', addclass:'datetimerange', autocomplete:false},
- {field: 'update_time', title: __('Update_time'), operate:'RANGE', addclass:'datetimerange', autocomplete:false},
- {field: 'delete_time', title: __('Delete_time'), operate:'RANGE', addclass:'datetimerange', autocomplete:false},
- {field: 'dispatch_type', title: __('Dispatch_type')},
- {field: 'receive_type', title: __('Receive_type')},
- {field: 'revisit_id', title: __('Revisit_id')},
- {field: 'dispatch_admin_id', title: __('Dispatch_admin_id')},
- {field: 'dispatch_admin_user', title: __('Dispatch_admin_user'), operate: 'LIKE'},
- {field: 'dispatch_time', title: __('Dispatch_time'), operate:'RANGE', addclass:'datetimerange', autocomplete:false},
- {field: 'aftersale_id', title: __('Aftersale_id')},
- {field: 'amount_images', title: __('Amount_images'), operate: false, events: Table.api.events.image, formatter: Table.api.formatter.images},
- {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
- ]
- ]
+
+ // 触发 tab 后发起 ajax 获取图表数据
+ $('ul.nav-tabs li.active a[data-toggle="tab"]').on("shown.bs.tab", function () {
+ getChartData();
});
- // 为表格绑定事件
- Table.api.bindevent(table);
+
+ function getChartData(){
+ // 获取单选框选中的值
+ var timeBy = $('input[name="filter[time_by]"]:checked').val();
+
+ // 获取日期范围值
+ var daterange = $('#daterange').val();
+
+ // 构建查询参数
+ var params = {
+ 'time_by': timeBy,
+ 'daterange': daterange
+ };
+
+ $.ajax({
+ url: "statistics/dispatcher/chartData", //
+ type: "POST",
+ dataType: "json",
+ data:params,
+ success: function (response) {
+ // 数据结构
+ // response = {
+ // source: [
+ // ['产品销售', '2015', '2016', '2017'],
+ // ['风扇', 43.3, 85.8, 93.7],
+ // ...
+ // ]
+ // }
+
+ var barChart = Echarts.init(document.getElementById('bar-chart'), 'walden');
+ var option = {
+ legend: {},
+ tooltip: {},
+ dataset: {
+ source: response
+ },
+ xAxis: { type: 'category' },
+ yAxis: {},
+ series: [
+ { type: 'bar' },
+ { type: 'bar' },
+ { type: 'bar' },
+ { type: 'bar' }
+ ]
+ };
+ barChart.setOption(option);
+ },
+ error: function () {
+ console.error("图表数据加载失败");
+ }
+ });
+ }
+
+
+
+
+ var form = $("#chart-filter");
+ var ranges = {};
+ ranges[__('Today')] = [Moment().startOf('day'), Moment().endOf('day')];
+ ranges[__('Yesterday')] = [Moment().subtract(1, 'days').startOf('day'), Moment().subtract(1, 'days').endOf('day')];
+ ranges[__('Last 7 Days')] = [Moment().subtract(6, 'days').startOf('day'), Moment().endOf('day')];
+ ranges[__('Last 30 Days')] = [Moment().subtract(29, 'days').startOf('day'), Moment().endOf('day')];
+ ranges[__('This Month')] = [Moment().startOf('month'), Moment().endOf('month')];
+ ranges[__('Last Month')] = [Moment().subtract(1, 'month').startOf('month'), Moment().subtract(1, 'month').endOf('month')];
+ ranges[__('今年')] = [Moment().startOf('year'), Moment().endOf('year')];
+ var options = {
+ timePicker: false,
+ autoUpdateInput: false,
+ timePickerSeconds: true,
+ timePicker24Hour: true,
+ autoApply: true,
+ locale: {
+ format: 'YYYY-MM-DD',
+ customRangeLabel: __("Custom Range"),
+ applyLabel: __("Apply"),
+ cancelLabel: __("Clear"),
+ },
+ ranges: ranges,
+ };
+ var callback = function (start, end) {
+ $(this.element).val(start.format(options.locale.format) + " - " + end.format(options.locale.format));
+ };
+ require(['bootstrap-daterangepicker'], function () {
+ $(".datetimerange", form).each(function () {
+ $(this).on('apply.daterangepicker', function (ev, picker) {
+ callback.call(picker, picker.startDate, picker.endDate);
+ var label = picker.chosenLabel;
+ $(picker.element).data('label', label).trigger("change");
+ });
+ $(this).on('cancel.daterangepicker', function (ev, picker) {
+ $(this).val('');
+ });
+ $(this).daterangepicker($.extend({}, options), callback);
+ });
+ });
+
+ // 手动触发一次激活 tab 的 shown.bs.tab
+ getChartData();
+ // 绑定查询按钮的点击事件
+ $('#filter-btn').on('click', function() {
+ getChartData();
+ });
},
+
add: function () {
Controller.api.bindevent();
},