Merge remote-tracking branch 'origin/feature/hant' into feature/hant

This commit is contained in:
hant 2025-05-06 18:15:20 +08:00
commit 16864b967b
26 changed files with 1356 additions and 667 deletions

View File

@ -194,4 +194,51 @@ if (!function_exists('build_heading')) {
}
return $result;
}
function getLocation($address){
try {
// 百度地图 API 的 URL 和 API Key
$apiKey = config('map.baidu_app_key'); // 替换为您的 API Key
// 构建请求 URL
$url = "http://api.map.baidu.com/geocoding/v3/?address=" . urlencode($address) . "&output=json&ak=" . $apiKey;
// 使用 cURL 请求接口
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
curl_close($ch);
// 解析返回的 JSON 数据
$data = json_decode($response, true);
// 获取经纬度
if ($data['status'] == 0) {
$latitude = $data['result']['location']['lat']; // 纬度
$longitude = $data['result']['location']['lng']; // 经度
return [
'lat' => $latitude,
'lng' => $longitude,
];
} else {
return [];
//echo "地址解析失败: " . $data['msg'] . "\n";
}
}catch (Exception $exception){
return [];
}
}
}

View File

@ -294,17 +294,17 @@ class Ajax extends Backend
$province = $this->request->get('province');
$city = $this->request->get('city');
}
$where = ['pid' => 0, 'level' => 1];
$where = ['level' => 1];
$provincelist = null;
if ($province !== null) {
$where['pid'] = $province;
$where['code'] = ['like',$province."%"];
$where['level'] = 2;
if ($city !== null) {
$where['pid'] = $city;
$where['code'] = ['like',$city."%"];
$where['level'] = 3;
}
}
$provincelist = Db::name('area')->where($where)->field('id as value,name')->select();
$provincelist = Db::name('areas')->where($where)->field('id as value,name')->select();
$this->success('', '', $provincelist);
}

View File

@ -390,6 +390,7 @@ class Order extends Backend
$order->status = \app\admin\model\Order::STATUS_DISPATCHED;
$order->dispatch_time = date('Y-m-d H:i:s');
// $order->dispatch_admin_id = $this->auth->id;
$order->worker_id = $worker_id;
$order->save();
//日志

View File

@ -54,17 +54,36 @@ class Aftersale extends Backend
$this->relationSearch = true;
//设置过滤方法
$this->request->filter(['strip_tags', 'trim']);
if ($this->request->isAjax()) {
$from = $this->request->param('from',1);
$man_id = $this->request->param('man_id');
//如果发送的来源是Selectpage则转发到Selectpage
if ($this->request->request('keyField')) {
return $this->selectpage();
}
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$list = $this->model
$builder = $this->model
->with(['order'])
->where($where)
->order($sort, $order)
->where($where);
if($from == 2){
//$builder->where('refund_amount','>',0);
//$builder->where('status','<>',-1);
$builder->where('fa_aftersale.dispatch_admin_id',$man_id ?: 0);
}
if($from == 3){
//$builder->where('refund_amount','>',0);
//$builder->where('status','<>',-1);
$builder->where('fa_aftersale.worker_id',$man_id ?: 0);
}
$list = $builder->order($sort, $order)
->paginate($limit);
foreach ($list as $row) {
@ -148,6 +167,10 @@ class Aftersale extends Backend
unset($params['company_refund_amount']);
unset($params['worker_refund_amount']);
}
$params['dispatch_admin_id'] = $order->dispatch_admin_id;
$params['dispatch_admin_user'] = $order->dispatch_admin_user;
$result = $this->model->allowField(true)->save($params);
$order->aftersale_id = $this->model->id;
$order->save();

View File

@ -155,6 +155,7 @@ class Dispatch extends Backend
$order->status = Order::STATUS_DISPATCHED;
$order->dispatch_time = date('Y-m-d H:i:s');
$order->dispatch_admin_id = $this->auth->id;
$order->worker_id = $worker->id;
$order->save();
//order log

View File

@ -19,14 +19,14 @@ class Dispatcher extends Backend
/**
* Staorder模型对象
* @var \app\admin\model\Order
* @var Order
*/
protected $model = null;
public function _initialize()
public function _initialize(): void
{
parent::_initialize();
$this->model = new \app\admin\model\Order();
$this->model = new Order();
}
@ -52,11 +52,14 @@ class Dispatcher extends Backend
$today = now()->format('Y-m-d');
$today_end = now()->format('Y-m-d');
//设置过滤方法
$this->request->filter(['strip_tags', 'trim']);
if (false === $this->request->isAjax()) {
$this->assign('daterange',$today.' - '.$today_end);
$this->assignconfig('default_daterange',now()->format('Y-m-d 00:00:00').' - '.now()->format('Y-m-d 23:59:59'));
return $this->view->fetch();
}
@ -99,7 +102,7 @@ class Dispatcher extends Backend
$newData = [
['派单员','总业绩(¥)','转化率(%)','利润率(%)','变现值']
['派单员','总业绩','转化率','利润率','变现值']
];
foreach ($data as $datum){
$newData[] = [
@ -114,10 +117,8 @@ class Dispatcher extends Backend
}
//图表统计
public function chart($filter,$getAll=false){
public function chart($filter,$getAll=false): \think\Collection|\think\Paginator|bool|array|string|\PDOStatement
{
$orderValid = implode(',',$this->model->tabStatus(Order::TAB_VALID));
@ -127,7 +128,7 @@ class Dispatcher extends Backend
"COUNT(CASE WHEN status = 60 THEN 1 END) AS finish_num", //完成数
"COUNT(CASE WHEN status IN (".$orderValid.") THEN 1 END) AS count_num", //总订单数 (排除取消 和草稿)
"SUM(CASE WHEN status = 60 THEN total END) AS total", //成效额
"SUM(CASE WHEN status = 60 THEN total END) AS performance", //业绩
"SUM(CASE WHEN status = 60 THEN performance END) AS performance", //业绩
"SUM(CASE WHEN status = 60 THEN (cost + material_cost) END) AS cost_total", //总成本
@ -164,7 +165,7 @@ class Dispatcher extends Backend
}
if($getAll){
$data = $builder->group('dispatch_admin_id')->select();
$data = $builder->group('dispatch_admin_id')->limit(50)->select();
}else{
$data = $builder->group('dispatch_admin_id')->paginate();
}
@ -189,7 +190,7 @@ class Dispatcher extends Backend
}else{
$datum->admin_user = '系统';
}
$datum->avg_time_diff = $this->_calc($datum->avg_time_diff,3600,2);
$datum->avg_time_diff = $this->_calc($datum->avg_time_diff,3600*24,2);
$datum->id = $datum->dispatch_admin_id;
$newData[] = $datum->toArray();
@ -208,10 +209,11 @@ class Dispatcher extends Backend
/**
* @param $a
* @param $b
* @param $scale
* @param int $scale
* @return int|string
*/
private function _calc($a,$b,$scale=4,$is_percent=false){
private function _calc($a, $b, int $scale=4, $is_percent=false): int|string
{
$val = $b > 0 ? bcdiv($a,$b,$scale) : 0;
if($is_percent){

View File

@ -0,0 +1,484 @@
<?php
namespace app\admin\controller\statistics;
use app\admin\model\Admin;
use app\admin\model\Aftersale;
use app\admin\model\Order;
use app\admin\model\OrderDispatch;
use app\admin\model\OrderReview;
use app\common\controller\Backend;
use PDOStatement;
use think\Collection;
use think\Exception;
use think\exception\DbException;
use think\Loader;
use think\response\Json;
/**
* 师傅列管理
*
* @icon fa fa-circle-o
*/
class Worker extends Backend
{
/**
* Sworker模型对象
* @var \app\admin\model\Worker
*/
protected $model = null;
/**
* @var Order
*/
private Order $OrderModel;
/**
* @var OrderDispatch
*/
private OrderDispatch $DispatchModel;
protected $relationSearch = true;
public function _initialize()
{
parent::_initialize();
$this->model = new \app\admin\model\Worker();
$this->OrderModel = new Order();
$this->DispatchModel = new OrderDispatch();
}
/**
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
*/
/**
* 查看
*
* @return string|Json
* @throws Exception
* @throws DbException
*/
public function index()
{
//设置过滤方法
$this->request->filter(['strip_tags', 'trim']);
if (false === $this->request->isAjax()) {
$appkey = config('map.baidu_app_key');
$this->assign('mapkey',$appkey);
$count = \app\admin\model\Worker::count();
$active_count = \app\admin\model\Worker::where('location_update_time', '>=', date('Y-m-d H:i:s', strtotime('-30 days')))
->count();
$add_count_1 = \app\admin\model\Worker::where('create_time', '>=', date('Y-m-d'))
->count();
$active_count_7 = \app\admin\model\Worker::where('create_time', '>=', date('Y-m-d H:i:s', strtotime('-7 days')))
->count();
$this->assign('count',$count);
$this->assign('active_count',$active_count);
$this->assign('add_count_1',$add_count_1);
$this->assign('add_count_7',$active_count_7);
//待接单
$todo_count = OrderDispatch::where('status',OrderDispatch::STATUS_TOGET)->count();
//进行中
$ing_count = OrderDispatch::whereIn('status',[OrderDispatch::STATUS_GOTIT,OrderDispatch::STATUS_PLANIT,OrderDispatch::STATUS_CLOCK])->count();
//待验收
$check_count = OrderDispatch::where('status',Order::STATUS_CHECKING)->count();
//售后待处理
$aftersale_count = Aftersale::where('status',1)->count();
$this->assign('todo_count',$todo_count);
$this->assign('ing_count',$ing_count);
$this->assign('check_count',$check_count);
$this->assign('aftersale_count',$aftersale_count);
//师傅坐标
$list = \app\admin\model\Worker::where('lng','>',0)->field(['lng','lat'])->select();
$arr = [];
foreach ($list as $item){
$arr[] = [
$item->lng, $item->lat,
];
}
$this->assign('locationData',json_encode($arr));
return $this->view->fetch();
}
//如果发送的来源是 Selectpage则转发到 Selectpage
if ($this->request->request('keyField')) {
return $this->selectpage();
}
$filter = $this->request->param('filter');
$filter = json_decode($filter,true);
if(!empty($filter['daterange'])){
$arr = explode(' - ',$filter['daterange']);
if(trim($arr[0])){
$filter['start_time'] = trim($arr[0]);
}
if(trim($arr[1])){
$filter['end_time'] = trim($arr[1]);
}
}
//派单表
$dispatchSubsql = $this->dispatchSubsql($filter);
//订单表
$orderSubsql = $this->oderSubsql($filter);
//评分表
$viewSubsql = $this->viewSubsql($filter);
[$where, $sort, $order, $offset, $limit] = $this->buildparams();
$list = $this->model->alias('fa_worker')
->with(['area','items'])
->field([
'fa_worker.*',
'IFNULL(a.dispatch_count, 0) AS dispatch_count',
'IFNULL(a.get_count, 0) AS get_count',
'IFNULL(a.refuse_count, 0) AS refuse_count',
'IFNULL(a.arrive_count, 0) AS arrive_count',
'IFNULL(a.avg_time_diff, 0) AS avg_time_diff',
'IFNULL(b.finish_num, 0) AS finish_num',
'IFNULL(b.total, 0) AS total',
'IFNULL(b.performance, 0) AS performance',
'IFNULL(b.refund_total, 0) AS refund_total',
'IFNULL(b.refund_count, 0) AS refund_count',
'IFNULL(b.cost, 0) AS cost',
'IFNULL(c.good_count, 0) AS good_count'
])
->join([$dispatchSubsql => 'a'], 'fa_worker.id = a.worker_id', 'LEFT')
->join([$orderSubsql => 'b'], 'fa_worker.id = b.worker_id', 'LEFT')
->join([$viewSubsql => 'c'], 'fa_worker.id = c.worker_id', 'LEFT')
->where($where)
->order($sort, $order)
->paginate($limit);
$this->_toList($list);
$result = ['total' => $list->total(), 'rows' => $list->items()];
return json($result);
}
/**
* @return bool|Collection|PDOStatement|string
* @throws DbException
*/
public function viewSubsql($filter=[]){
//评分表
$viewSubsql = OrderReview::where('worker_star',5)->field(['worker_id','count(*) as good_count']);
if(!empty($filter['start_time'])){
$viewSubsql->where('create_time','>=',$filter['start_time']);
}
if(!empty($filter['end_time'])){
$viewSubsql->where('create_time','<=',$filter['start_time']);
}
return $viewSubsql->group('worker_id')->buildSql();
}
/**
* @return bool|PDOStatement|string|Collection
* @throws DbException
*/
public function dispatchSubsql($filter): Collection|bool|string|PDOStatement
{
$builder = new OrderDispatch();
$fields = [
'worker_id',
"COUNT(*) AS dispatch_count", //分配数
"COUNT(CASE WHEN status NOT IN (0, -10) THEN 1 END) AS get_count", //接单数
//"COUNT(CASE WHEN status IN (60) THEN 1 END) AS finish_count", //完成数
"COUNT(CASE WHEN status NOT IN (-10) THEN 1 END) AS refuse_count", //拒绝数
"COUNT(arrive_time) AS arrive_count", //上门数
"AVG(CASE WHEN status = 60 AND arrive_time IS NOT NULL THEN UNIX_TIMESTAMP(arrive_time) - UNIX_TIMESTAMP(create_time) END) AS avg_time_diff", //联系时效
];
$builder->field($fields);
if(!empty($filter['start_time'])){
$builder->where('create_time','>=',$filter['start_time']);
}
if(!empty($filter['end_time'])){
$builder->where('create_time','<=',$filter['start_time']);
}
$builder->group('worker_id');
return $builder->buildSql();
}
//图表统计
/**
* @throws DbException
*/
public function oderSubsql($filter = []): Collection|bool|string|PDOStatement
{
$orderValid = implode(',',(new Order())->tabStatus(Order::TAB_VALID));
//"COUNT(CASE WHEN status IN (".$orderValid.") THEN 1 END) AS ing_num",
$fields = [
'worker_id',
"COUNT(CASE WHEN status = 60 THEN 1 END) AS finish_num", //完成数
//"COUNT(CASE WHEN status IN (".$orderValid.") THEN 1 END) AS count_num", //总订单数 (排除取消 和草稿)
"SUM(CASE WHEN status = 60 THEN total END) AS total", //成效额
"SUM(CASE WHEN status = 60 THEN performance END) AS performance", //业绩
"SUM(CASE WHEN status = 60 THEN cost END) AS cost", //成效额
// "SUM(CASE WHEN status = 60 THEN (cost + material_cost) END) AS cost_total", //总成本
"SUM(CASE WHEN status = 60 THEN (refund_amount + worker_refund_amount) END) AS refund_total", //退款总数
"COUNT(CASE WHEN refund_amount > 0 OR worker_refund_amount > 0 THEN 1 END) AS refund_count", //退款订单数量
//"AVG(CASE WHEN status > 10 THEN UNIX_TIMESTAMP(dispatch_time) - UNIX_TIMESTAMP(create_time) END) AS avg_time_diff", //派单时效
// "SUM(CASE WHEN status = 60 THEN (field1 + field2) END) AS performance",
];
$builder = (new Order())->field($fields);
if(!empty($filter['start_time'])){
$builder->where('create_time','>=',$filter['start_time']);
}
if(!empty($filter['end_time'])){
$builder->where('create_time','<=',$filter['start_time']);
}
//->where('dispatch_admin_id','>',0);
return $builder->group('worker_id')->buildSql();
}
/**
* @param $a
* @param $b
* @param int $scale
* @param bool $is_percent
* @return int|string
*/
private function _calc($a, $b, int $scale=4, bool $is_percent=false): int|string
{
$a = $a??0;
$b = $b??0;
$val = $b > 0 ? bcdiv($a,$b,$scale) : '0.00';
if($is_percent){
return bcmul($val,100,2);
}
return $val;
}
private function _toList(\think\Paginator $list)
{
foreach ($list as &$datum){
//利润率 = 总业绩/总成效额
$datum->performance_rate = $this->_calc($datum->performance,$datum->total,4,true);
//转化率 = 完单数 / 总接单数
$datum->trans_rate = $this->_calc($datum->finish_num,$datum->get_count,4,true);
//变现值 = 总业绩 / 总接单数
$datum->cash_value = $this->_calc($datum->performance,$datum->get_count,2);
//拒单率 = 拒绝数 / 派单数
$datum->refuse_rate = $this->_calc($datum->refuse_count,$datum->dispatch_count,4,true);
//上门率 = 打卡数 / 接单数
$datum->arrive_rate = $this->_calc($datum->arrive_count,$datum->get_count,4,true);
//好评率
$datum->good_rate = $this->_calc($datum->good_count,$datum->finish_num,4,true);
$datum->avg_time_diff = $this->_calc($datum->avg_time_diff,3600*24,2);
}
}
/**
* 生成查询所需要的条件,排序方式
* @param mixed $searchfields 快速查询的字段
* @param boolean $relationSearch 是否关联查询
* @return array
*/
protected function buildparams($searchfields = null, $relationSearch = null)
{
$searchfields = is_null($searchfields) ? $this->searchFields : $searchfields;
$relationSearch = is_null($relationSearch) ? $this->relationSearch : $relationSearch;
$search = $this->request->get("search", '');
$filter = $this->request->get("filter", '');
$op = $this->request->get("op", '', 'trim');
$sort = $this->request->get("sort", !empty($this->model) && $this->model->getPk() ? $this->model->getPk() : 'id');
$order = $this->request->get("order", "DESC");
$offset = max(0, $this->request->get("offset/d", 0));
$limit = max(0, $this->request->get("limit/d", 0));
$limit = $limit ?: 999999;
//新增自动计算页码
$page = $limit ? intval($offset / $limit) + 1 : 1;
if ($this->request->has("page")) {
$page = max(0, $this->request->get("page/d", 1));
}
$this->request->get([config('paginate.var_page') => $page]);
$filter = (array)json_decode($filter, true);
unset($filter['daterange']);
$op = (array)json_decode($op, true);
$filter = $filter ? $filter : [];
$where = [];
$alias = [];
$bind = [];
$name = '';
$aliasName = '';
if (!empty($this->model) && $relationSearch) {
$name = $this->model->getTable();
$alias[$name] = Loader::parseName(basename(str_replace('\\', '/', get_class($this->model))));
$aliasName = $alias[$name] . '.';
}
$sortArr = explode(',', $sort);
foreach ($sortArr as $index => & $item) {
$item = stripos($item, ".") === false ? $aliasName . trim($item) : $item;
}
unset($item);
$sort = implode(',', $sortArr);
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds)) {
$where[] = [$aliasName . $this->dataLimitField, 'in', $adminIds];
}
if ($search) {
$searcharr = is_array($searchfields) ? $searchfields : explode(',', $searchfields);
foreach ($searcharr as $k => &$v) {
$v = stripos($v, ".") === false ? $aliasName . $v : $v;
}
unset($v);
$where[] = [implode("|", $searcharr), "LIKE", "%{$search}%"];
}
$index = 0;
foreach ($filter as $k => $v) {
if (!preg_match('/^[a-zA-Z0-9_\-\.]+$/', $k)) {
continue;
}
$sym = $op[$k] ?? '=';
if (stripos($k, ".") === false) {
$k = $aliasName . $k;
}
$v = !is_array($v) ? trim($v) : $v;
$sym = strtoupper($op[$k] ?? $sym);
//null和空字符串特殊处理
if (!is_array($v)) {
if (in_array(strtoupper($v), ['NULL', 'NOT NULL'])) {
$sym = strtoupper($v);
}
if (in_array($v, ['""', "''"])) {
$v = '';
$sym = '=';
}
}
switch ($sym) {
case '=':
case '<>':
$where[] = [$k, $sym, (string)$v];
break;
case 'LIKE':
case 'NOT LIKE':
case 'LIKE %...%':
case 'NOT LIKE %...%':
$where[] = [$k, trim(str_replace('%...%', '', $sym)), "%{$v}%"];
break;
case '>':
case '>=':
case '<':
case '<=':
$where[] = [$k, $sym, intval($v)];
break;
case 'FINDIN':
case 'FINDINSET':
case 'FIND_IN_SET':
$v = is_array($v) ? $v : explode(',', str_replace(' ', ',', $v));
$findArr = array_values($v);
foreach ($findArr as $idx => $item) {
$bindName = "item_" . $index . "_" . $idx;
$bind[$bindName] = $item;
$where[] = "FIND_IN_SET(:{$bindName}, `" . str_replace('.', '`.`', $k) . "`)";
}
break;
case 'IN':
case 'IN(...)':
case 'NOT IN':
case 'NOT IN(...)':
$where[] = [$k, str_replace('(...)', '', $sym), is_array($v) ? $v : explode(',', $v)];
break;
case 'BETWEEN':
case 'NOT BETWEEN':
$arr = array_slice(explode(',', $v), 0, 2);
if (stripos($v, ',') === false || !array_filter($arr, function ($v) {
return $v != '' && $v !== false && $v !== null;
})) {
continue 2;
}
//当出现一边为空时改变操作符
if ($arr[0] === '') {
$sym = $sym == 'BETWEEN' ? '<=' : '>';
$arr = $arr[1];
} elseif ($arr[1] === '') {
$sym = $sym == 'BETWEEN' ? '>=' : '<';
$arr = $arr[0];
}
$where[] = [$k, $sym, $arr];
break;
case 'RANGE':
case 'NOT RANGE':
$v = str_replace(' - ', ',', $v);
$arr = array_slice(explode(',', $v), 0, 2);
if (stripos($v, ',') === false || !array_filter($arr)) {
continue 2;
}
//当出现一边为空时改变操作符
if ($arr[0] === '') {
$sym = $sym == 'RANGE' ? '<=' : '>';
$arr = $arr[1];
} elseif ($arr[1] === '') {
$sym = $sym == 'RANGE' ? '>=' : '<';
$arr = $arr[0];
}
$tableArr = explode('.', $k);
if (count($tableArr) > 1 && $tableArr[0] != $name && !in_array($tableArr[0], $alias)
&& !empty($this->model) && $this->relationSearch) {
//修复关联模型下时间无法搜索的BUG
$relation = Loader::parseName($tableArr[0], 1, false);
$alias[$this->model->$relation()->getTable()] = $tableArr[0];
}
$where[] = [$k, str_replace('RANGE', 'BETWEEN', $sym) . ' TIME', $arr];
break;
case 'NULL':
case 'IS NULL':
case 'NOT NULL':
case 'IS NOT NULL':
$where[] = [$k, strtolower(str_replace('IS ', '', $sym))];
break;
default:
break;
}
$index++;
}
if (!empty($this->model)) {
$this->model->alias($alias);
}
$model = $this->model;
$where = function ($query) use ($where, $alias, $bind, &$model) {
if (!empty($model)) {
$model->alias($alias);
$model->bind($bind);
}
foreach ($where as $k => $v) {
if (is_array($v)) {
call_user_func_array([$query, 'where'], $v);
} else {
$query->where($v);
}
}
};
return [$where, $sort, $order, $offset, $limit, $page, $alias, $bind];
}
}

View File

@ -2,6 +2,7 @@
namespace app\admin\controller\workers;
use app\admin\model\Area;
use app\admin\model\AuthGroup;
use app\admin\model\Item;
use app\admin\model\Order;
@ -158,6 +159,18 @@ class Worker extends Backend
$this->model->validateFailException()->validate($validate);
}
$params['admin_id'] = $this->auth->id;
if(!empty($params['area_id'])){
$area = Area::getByCode($params['area_id']);
if($area){
$location = getLocation($area->merge_name);
if(!empty($location)){
$params['lng'] = $location['lng'];
$params['lat'] = $location['lat'];
}
}
}
$result = $this->model->allowField(true)->save($params);
$item_map = model('item')->getAll();
$item_map = array_column($item_map, 'level', 'id');

View File

@ -0,0 +1,22 @@
<?php
return [
'Id' => 'ID',
'Type' => '1 自营 2 非自营',
'Name' => '师傅姓名',
'Tel' => '师傅电话',
'Status' => '师傅状态',
'Status 1' => '激活',
'Set status to 1' => '设为激活',
'Status 0' => '冻结',
'Set status to 0' => '设为冻结',
'Area_id' => '所在地区',
'Lng' => '经度',
'Lat' => '纬度',
'Location_update_time' => '位置更新时间',
'Deposit_amount' => '保证金金额',
'Star' => '信用(5星制)',
'Create_time' => '创建时间',
'Update_time' => '更新时间',
'Deletetime' => '删除时间'
];

View File

@ -47,7 +47,7 @@ class Worker extends BaseModel
public function area()
{
return $this->belongsTo('Area', 'area_id', 'area_code');
return $this->belongsTo('area', 'area_id', 'area_code')->setEagerlyType(0);
}
public function admin()
@ -62,4 +62,12 @@ class Worker extends BaseModel
}
// 定义通过中间表与 roles 表的一对多关系
public function items()
{
return $this->hasManyThrough(Item::class, WorkerItem::class, 'worker_id', 'item_id', 'id', 'id');
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace app\admin\validate;
use think\Validate;
class Sworker extends Validate
{
/**
* 验证规则
*/
protected $rule = [
];
/**
* 提示消息
*/
protected $message = [
];
/**
* 验证场景
*/
protected $scene = [
'add' => [],
'edit' => [],
];
}

View File

@ -22,14 +22,14 @@
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('aftersales/aftersale/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
-->
<div class="dropdown btn-group {:$auth->check('aftersales/aftersale/multi')?'':'hide'}">
<!-- <div class="dropdown btn-group {:$auth->check('aftersales/aftersale/multi')?'':'hide'}">
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
<ul class="dropdown-menu text-left" role="menu">
{foreach name="statusList" item="vo"}
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:" data-params="status={$key}">{:__('Set status to ' . $key)}</a></li>
{/foreach}
</ul>
</div>
</div>-->
</div>

View File

@ -1,313 +0,0 @@
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Order_no')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-order_no" data-rule="required" class="form-control" name="row[order_no]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Customer')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-customer" class="form-control" name="row[customer]" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Tel')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-tel" data-rule="required" class="form-control" name="row[tel]" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="radio">
{foreach name="statusList" item="vo"}
<label for="row[status]-{$key}"><input id="row[status]-{$key}" name="row[status]" type="radio" value="{$key}" {in name="key" value="0"}checked{/in} /> {$vo}</label>
{/foreach}
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Area_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-area_id" data-rule="required" data-source="area/index" class="form-control selectpage" name="row[area_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Address')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-address" data-rule="required" class="form-control" name="row[address]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Lng')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-lng" class="form-control" step="0.000001" name="row[lng]" type="number">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Lat')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-lat" class="form-control" step="0.000001" name="row[lat]" type="number">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Work_tel_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-work_tel_id" data-rule="required" data-source="work/tel/index" class="form-control selectpage" name="row[work_tel_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Source_shop')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-source_shop" data-rule="required" class="form-control" name="row[source_shop]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Source')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-source" data-rule="required" class="form-control" name="row[source]" type="number" value="0">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Source_uid')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-source_uid" class="form-control" name="row[source_uid]" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Item_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-item_id" data-rule="required" data-source="item/index" class="form-control selectpage" name="row[item_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Item_title')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-item_title" data-rule="required" class="form-control" name="row[item_title]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Detail')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-detail" class="form-control" name="row[detail]" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-remark" class="form-control" name="row[remark]" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Images')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-images" class="form-control" size="50" name="row[images]" type="text">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="faupload-images" class="btn btn-danger faupload" data-input-id="c-images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp,image/webp" data-multiple="true" data-preview-id="p-images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-images" class="btn btn-primary fachoose" data-input-id="c-images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-images"></span>
</div>
<ul class="row list-inline faupload-preview" id="p-images"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Plan_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-plan_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[plan_time]" type="text" value="{:date('Y-m-d H:i:s')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Coupon_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-coupon_id" data-rule="required" data-source="coupon/index" class="form-control selectpage" name="row[coupon_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Total')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-total" data-rule="required" class="form-control" step="0.01" name="row[total]" type="number" value="0.00">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Online_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-online_amount" data-rule="required" class="form-control" step="0.01" name="row[online_amount]" type="number" value="0.00">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Offline_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-offline_amount" data-rule="required" class="form-control" step="0.01" name="row[offline_amount]" type="number" value="0.00">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Online_amount_last')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-online_amount_last" data-rule="required" class="form-control" step="0.01" name="row[online_amount_last]" type="number" value="0.00">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Offline_amount_type')}:</label>
<div class="col-xs-12 col-sm-8">
<select id="c-offline_amount_type" data-rule="required" class="form-control selectpicker" name="row[offline_amount_type]">
{foreach name="offlineAmountTypeList" item="vo"}
<option value="{$key}" {in name="key" value="0"}selected{/in}>{$vo}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Discount_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-discount_amount" data-rule="required" class="form-control" step="0.01" name="row[discount_amount]" type="number" value="0.00">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Refund_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-refund_amount" data-rule="required" class="form-control" step="0.01" name="row[refund_amount]" type="number" value="0.00">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Worker_refund_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-worker_refund_amount" data-rule="required" class="form-control" step="0.01" name="row[worker_refund_amount]" type="number" value="0.00">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Real_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-real_amount" data-rule="required" class="form-control" step="0.01" name="row[real_amount]" type="number" value="0.00">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Cost')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-cost" data-rule="required" class="form-control" step="0.01" name="row[cost]" type="number" value="0.00">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Material_cost')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-material_cost" data-rule="required" class="form-control" step="0.01" name="row[material_cost]" type="number" value="0.00">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Performance')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-performance" data-rule="required" class="form-control" step="0.01" name="row[performance]" type="number" value="0.00">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Cancel_reason_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-cancel_reason_id" data-rule="required" data-source="cancel/reason/index" class="form-control selectpage" name="row[cancel_reason_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Cancel_detail')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-cancel_detail" class="form-control" name="row[cancel_detail]" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Audit_remark')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-audit_remark" class="form-control" name="row[audit_remark]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Audit_admin_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-audit_admin_id" data-rule="required" data-source="audit/admin/index" class="form-control selectpage" name="row[audit_admin_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Create_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-create_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[create_time]" type="text" value="{:date('Y-m-d H:i:s')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Update_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-update_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[update_time]" type="text" value="{:date('Y-m-d H:i:s')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Delete_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-delete_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[delete_time]" type="text" value="{:date('Y-m-d H:i:s')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Dispatch_type')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-dispatch_type" class="form-control" name="row[dispatch_type]" type="number">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Receive_type')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-receive_type" class="form-control" name="row[receive_type]" type="number">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Revisit_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-revisit_id" data-rule="required" data-source="revisit/index" class="form-control selectpage" name="row[revisit_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Dispatch_admin_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-dispatch_admin_id" data-rule="required" data-source="dispatch/admin/index" class="form-control selectpage" name="row[dispatch_admin_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Dispatch_admin_user')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-dispatch_admin_user" class="form-control" name="row[dispatch_admin_user]" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Dispatch_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-dispatch_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[dispatch_time]" type="text" value="{:date('Y-m-d H:i:s')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Aftersale_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-aftersale_id" data-rule="required" data-source="aftersale/index" class="form-control selectpage" name="row[aftersale_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Amount_images')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-amount_images" class="form-control" size="50" name="row[amount_images]" type="text">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="faupload-amount_images" class="btn btn-danger faupload" data-input-id="c-amount_images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp,image/webp" data-multiple="true" data-preview-id="p-amount_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-amount_images" class="btn btn-primary fachoose" data-input-id="c-amount_images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-amount_images"></span>
</div>
<ul class="row list-inline faupload-preview" id="p-amount_images"></ul>
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
</div>
</div>
</form>

View File

@ -1,313 +0,0 @@
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Order_no')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-order_no" data-rule="required" class="form-control" name="row[order_no]" type="text" value="{$row.order_no|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Customer')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-customer" class="form-control" name="row[customer]" type="text" value="{$row.customer|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Tel')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-tel" data-rule="required" class="form-control" name="row[tel]" type="text" value="{$row.tel|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="radio">
{foreach name="statusList" item="vo"}
<label for="row[status]-{$key}"><input id="row[status]-{$key}" name="row[status]" type="radio" value="{$key}" {in name="key" value="$row.status"}checked{/in} /> {$vo}</label>
{/foreach}
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Area_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-area_id" data-rule="required" data-source="area/index" class="form-control selectpage" name="row[area_id]" type="text" value="{$row.area_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Address')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-address" data-rule="required" class="form-control" name="row[address]" type="text" value="{$row.address|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Lng')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-lng" class="form-control" step="0.000001" name="row[lng]" type="number" value="{$row.lng|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Lat')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-lat" class="form-control" step="0.000001" name="row[lat]" type="number" value="{$row.lat|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Work_tel_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-work_tel_id" data-rule="required" data-source="work/tel/index" class="form-control selectpage" name="row[work_tel_id]" type="text" value="{$row.work_tel_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Source_shop')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-source_shop" data-rule="required" class="form-control" name="row[source_shop]" type="text" value="{$row.source_shop|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Source')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-source" data-rule="required" class="form-control" name="row[source]" type="number" value="{$row.source|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Source_uid')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-source_uid" class="form-control" name="row[source_uid]" type="text" value="{$row.source_uid|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Item_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-item_id" data-rule="required" data-source="item/index" class="form-control selectpage" name="row[item_id]" type="text" value="{$row.item_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Item_title')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-item_title" data-rule="required" class="form-control" name="row[item_title]" type="text" value="{$row.item_title|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Detail')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-detail" class="form-control" name="row[detail]" type="text" value="{$row.detail|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-remark" class="form-control" name="row[remark]" type="text" value="{$row.remark|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Images')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-images" class="form-control" size="50" name="row[images]" type="text" value="{$row.images|htmlentities}">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="faupload-images" class="btn btn-danger faupload" data-input-id="c-images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp,image/webp" data-multiple="true" data-preview-id="p-images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-images" class="btn btn-primary fachoose" data-input-id="c-images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-images"></span>
</div>
<ul class="row list-inline faupload-preview" id="p-images"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Plan_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-plan_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[plan_time]" type="text" value="{$row.plan_time}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Coupon_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-coupon_id" data-rule="required" data-source="coupon/index" class="form-control selectpage" name="row[coupon_id]" type="text" value="{$row.coupon_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Total')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-total" data-rule="required" class="form-control" step="0.01" name="row[total]" type="number" value="{$row.total|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Online_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-online_amount" data-rule="required" class="form-control" step="0.01" name="row[online_amount]" type="number" value="{$row.online_amount|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Offline_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-offline_amount" data-rule="required" class="form-control" step="0.01" name="row[offline_amount]" type="number" value="{$row.offline_amount|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Online_amount_last')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-online_amount_last" data-rule="required" class="form-control" step="0.01" name="row[online_amount_last]" type="number" value="{$row.online_amount_last|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Offline_amount_type')}:</label>
<div class="col-xs-12 col-sm-8">
<select id="c-offline_amount_type" data-rule="required" class="form-control selectpicker" name="row[offline_amount_type]">
{foreach name="offlineAmountTypeList" item="vo"}
<option value="{$key}" {in name="key" value="$row.offline_amount_type"}selected{/in}>{$vo}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Discount_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-discount_amount" data-rule="required" class="form-control" step="0.01" name="row[discount_amount]" type="number" value="{$row.discount_amount|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Refund_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-refund_amount" data-rule="required" class="form-control" step="0.01" name="row[refund_amount]" type="number" value="{$row.refund_amount|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Worker_refund_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-worker_refund_amount" data-rule="required" class="form-control" step="0.01" name="row[worker_refund_amount]" type="number" value="{$row.worker_refund_amount|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Real_amount')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-real_amount" data-rule="required" class="form-control" step="0.01" name="row[real_amount]" type="number" value="{$row.real_amount|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Cost')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-cost" data-rule="required" class="form-control" step="0.01" name="row[cost]" type="number" value="{$row.cost|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Material_cost')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-material_cost" data-rule="required" class="form-control" step="0.01" name="row[material_cost]" type="number" value="{$row.material_cost|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Performance')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-performance" data-rule="required" class="form-control" step="0.01" name="row[performance]" type="number" value="{$row.performance|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Cancel_reason_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-cancel_reason_id" data-rule="required" data-source="cancel/reason/index" class="form-control selectpage" name="row[cancel_reason_id]" type="text" value="{$row.cancel_reason_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Cancel_detail')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-cancel_detail" class="form-control" name="row[cancel_detail]" type="text" value="{$row.cancel_detail|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Audit_remark')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-audit_remark" class="form-control" name="row[audit_remark]" type="text" value="{$row.audit_remark|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Audit_admin_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-audit_admin_id" data-rule="required" data-source="audit/admin/index" class="form-control selectpage" name="row[audit_admin_id]" type="text" value="{$row.audit_admin_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Create_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-create_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[create_time]" type="text" value="{$row.create_time}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Update_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-update_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[update_time]" type="text" value="{$row.update_time}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Delete_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-delete_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[delete_time]" type="text" value="{$row.delete_time}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Dispatch_type')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-dispatch_type" class="form-control" name="row[dispatch_type]" type="number" value="{$row.dispatch_type|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Receive_type')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-receive_type" class="form-control" name="row[receive_type]" type="number" value="{$row.receive_type|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Revisit_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-revisit_id" data-rule="required" data-source="revisit/index" class="form-control selectpage" name="row[revisit_id]" type="text" value="{$row.revisit_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Dispatch_admin_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-dispatch_admin_id" data-rule="required" data-source="dispatch/admin/index" class="form-control selectpage" name="row[dispatch_admin_id]" type="text" value="{$row.dispatch_admin_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Dispatch_admin_user')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-dispatch_admin_user" class="form-control" name="row[dispatch_admin_user]" type="text" value="{$row.dispatch_admin_user|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Dispatch_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-dispatch_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[dispatch_time]" type="text" value="{$row.dispatch_time}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Aftersale_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-aftersale_id" data-rule="required" data-source="aftersale/index" class="form-control selectpage" name="row[aftersale_id]" type="text" value="{$row.aftersale_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Amount_images')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-amount_images" class="form-control" size="50" name="row[amount_images]" type="text" value="{$row.amount_images|htmlentities}">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="faupload-amount_images" class="btn btn-danger faupload" data-input-id="c-amount_images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp,image/webp" data-multiple="true" data-preview-id="p-amount_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-amount_images" class="btn btn-primary fachoose" data-input-id="c-amount_images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-amount_images"></span>
</div>
<ul class="row list-inline faupload-preview" id="p-amount_images"></ul>
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,361 @@
<style>
.sm-st {
background: #fff;
padding: 20px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
margin-bottom: 20px;
}
.sm-st-icon {
width: 60px;
height: 60px;
display: inline-block;
line-height: 60px;
text-align: center;
font-size: 30px;
background: #eee;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
float: left;
margin-right: 10px;
color: #fff;
}
.sm-st-info {
padding-top: 2px;
}
.sm-st-info span {
display: block;
font-size: 14px;
font-weight: 600;
}
.orange {
background: #fa8564 !important;
}
.tar {
background: #45cf95 !important;
}
.sm-st .green {
background: #86ba41 !important;
}
.pink {
background: #AC75F0 !important;
}
.yellow-b {
background: #fdd752 !important;
}
.stat-elem {
background-color: #fff;
padding: 18px;
border-radius: 40px;
}
.stat-info {
text-align: center;
background-color: #fff;
border-radius: 5px;
margin-top: -5px;
padding: 8px;
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.05);
font-style: italic;
}
.stat-icon {
text-align: center;
margin-bottom: 5px;
}
.st-red {
background-color: #F05050;
}
.st-green {
background-color: #27C24C;
}
.st-violet {
background-color: #7266ba;
}
.st-blue {
background-color: #23b7e5;
}
.stats .stat-icon {
color: #28bb9c;
display: inline-block;
font-size: 26px;
text-align: center;
vertical-align: middle;
width: 50px;
float: left;
}
.stat {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
}
.stat .value {
font-size: 20px;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
}
.stat .name {
overflow: hidden;
text-overflow: ellipsis;
margin: 5px 0;
}
.stat.lg .value {
font-size: 26px;
line-height: 28px;
}
.stat-col {
margin:0 0 10px 0;
}
.stat.lg .name {
font-size: 16px;
}
.stat-col .progress {
height: 2px;
}
.stat-col .progress-bar {
line-height: 2px;
height: 2px;
}
.item {
padding: 30px 0;
}
#statistics .panel {
min-height: 150px;
}
#statistics .panel h5 {
font-size: 14px;
}
</style>
<div class="panel panel-default panel-intro">
<div class="panel-heading">
<!--<div class="panel-lead"><em>多表格Multitable</em>用于展示在一个页面展示多个表格数据,并且每次切换时刷新</div>-->
<ul class="nav nav-tabs">
<li class="active"><a data-val="first" href="#first" data-toggle="tab">统计图表</a></li>
<li><a href="#second" data-val="second" data-toggle="tab">统计列表</a></li>
</ul>
</div>
<div class="panel-body" style="height: 100%">
<div id="myTabContent" class="tab-content" style="height: 100%">
<div class="tab-pane fade active in" style="height: 100%" id="first">
<section class="connectedSortable" style="height: 100%">
<div class="nav-tabs-custom charts-custom">
<!-- Tabs within a box -->
<ul class="nav nav-tabs pull-right">
<li class="active"><a href="#bar-chart" data-toggle="tab">地图分布</a></li>
<!-- <li><a href="#pie-chart" data-toggle="tab">饼图</a></li>-->
<li class="pull-left header"><i class="fa fa-inbox"></i> 师傅统计图</li>
</ul>
<div class="row">
<div class="col-sm-3 col-xs-6">
<div class="sm-st clearfix">
<span class="sm-st-icon st-blue"><i class="fa fa-users"></i></span>
<div class="sm-st-info">
<span>{$count}/{$active_count}</span>
师傅总数/活跃数
</div>
</div>
</div>
<div class="col-sm-3 col-xs-6">
<div class="sm-st clearfix">
<span class="sm-st-icon st-green"><i class="fa fa-users"></i></span>
<div class="sm-st-info">
<span>{$add_count_1}/{$add_count_7}</span>
今日新增/7天新增
</div>
</div>
</div>
<div class="col-sm-3 col-xs-6">
<div class="sm-st clearfix">
<span class="sm-st-icon st-violet"><i class="fa fa-users"></i></span>
<div class="sm-st-info">
<span>{$todo_count}/{$ing_count}/{$check_count}</span>
待接单/进行中/待验收
</div>
</div>
</div>
<div class="col-sm-3 col-xs-6">
<div class="sm-st clearfix">
<span class="sm-st-icon st-red"><i class="fa fa-users"></i></span>
<div class="sm-st-info">
<span>{$aftersale_count}</span>
待处理售后
</div>
</div>
</div>
</div>
<div class="tab-content no-padding" style="height: 100%">
<div class="chart tab-pane active" id="bar-chart" style="width: 100%; min-height:600px;height: 100%">
</div>
</div>
</div>
</section>
</div>
<div class="tab-pane fade" id="second">
<table id="table2" class="table table-striped table-bordered table-hover" width="100%">
</table>
</div>
</div>
</div>
</div>
<!-- 先加载 mapv -->
<script src="https://mapv.baidu.com/build/mapv.min.js"></script>
<!-- 再加载 mapvgl -->
<script src="https://code.bdstatic.com/npm/mapvgl@1.0.0-beta.189/dist/mapvgl.min.js"></script>
<!-- 最后加载百度地图并回调 -->
<!-- 加载百度地图 GL -->
<script>
const rawDataStr = '{$locationData}';
// 先解析成数组
const rawData = JSON.parse(rawDataStr);
window.initMap = function () {
const map = new BMapGL.Map("bar-chart");
map.centerAndZoom(new BMapGL.Point(104.0665, 30.5728), 6);
map.enableScrollWheelZoom(true);
map.addEventListener('tilesloaded', function () {
const view = new mapvgl.View({ map });
// ✅ 写死的点数据10个城市
/*const data = [
{ geometry: { type: 'Point', coordinates: [116.4074, 39.9042] } }, // 北京
{ geometry: { type: 'Point', coordinates: [121.4737, 31.2304] } }, // 上海
{ geometry: { type: 'Point', coordinates: [113.2644, 23.1291] } }, // 广州
{ geometry: { type: 'Point', coordinates: [114.0579, 22.5431] } }, // 深圳
{ geometry: { type: 'Point', coordinates: [117.2000, 39.1333] } }, // 天津
{ geometry: { type: 'Point', coordinates: [120.1551, 30.2741] } }, // 杭州
{ geometry: { type: 'Point', coordinates: [104.0665, 30.5728] } }, // 成都
{ geometry: { type: 'Point', coordinates: [106.5516, 29.5630] } }, // 重庆
{ geometry: { type: 'Point', coordinates: [108.9480, 34.2632] } }, // 西安
{ geometry: { type: 'Point', coordinates: [115.8582, 28.6829] } } // 南昌
];*/
const data = rawData.map(item => ({
geometry: {
type: 'Point',
coordinates: item
}
}));
// 根据聚合区域内的点数决定颜色(数量多,颜色深)
const gradient = {
0: 'rgba(162, 180, 73, 0.7)', // 浅绿
0.3: 'rgba(121, 153, 0, 0.8)', // 中绿
0.7: 'rgba(99, 121, 0, 0.9)', // 深绿
1.0: 'rgba(66, 77, 0, 1)' // 更深绿
};
// 定义聚合层
const clusterLayer = new mapvgl.ClusterLayer({
minSize: 30,
maxSize: 50,
clusterRadius: 150,
gradient: gradient, // 设置颜色渐变
showText: true,
textOptions: {
fontSize: 12,
color: 'white',
format: function (count) {
if (count === 0) return ''; // 数量为0时不展示
return count >= 1000 ? Math.round(count / 100) / 10 + 'k' : count; // 格式化数字
}
},
iconOptions: {
fillStyle: 'rgba(162, 180, 73, 0.7)', // 主色调:浅绿色
lineWidth: 1,
strokeStyle: 'rgba(121, 153, 0, 1)', // 边框:绿色
radius: 19, // 圆形点
shadowBlur: 5, // 阴影效果
shadowColor: 'rgba(244, 255, 204, 0.7)', // 阴影颜色
},
// 让每个点有自己的外观样式
icon: function (count) {
// 使用样式文本:点的数量越多,颜色越深
return {
backgroundColor: `rgba(121, 153, 0, 0.7)`, // 点背景颜色
height: '38px', // 高度
width: '38px', // 宽度
border: '1px solid rgb(121, 153, 0)', // 边框
borderRadius: '19px', // 边角半径(圆形)
boxShadow: 'rgb(244, 255, 204) 0px 0px 5px', // 阴影
lineHeight: '38px', // 行高
color: 'rgb(244, 255, 204)', // 字体颜色(淡黄)
fontSize: '14px', // 字体大小
textAlign: 'center', // 文本居中
cursor: 'pointer' // 鼠标样式为指针
};
}
});
// 添加数据并展示
view.addLayer(clusterLayer);
clusterLayer.setData(data);
});
};
</script>
<script src="https://api.map.baidu.com/api?type=webgl&v=1.0&ak={$mapkey}&callback=initMap"></script>
<!--
<script id="sourcetpl" type="text/html">
<div class="row">
<div class="col-xs-12">
<div class="form-inline" data-toggle="cxselect" data-selects="province,city,area">
<select style="width: 100px;" class="province form-control" name="province_id" data-url="ajax/area" ></select>
<input type="hidden" class="operate" data-name="province_id" value="=" />
<select style="width: 100px;" class="city form-control" name="city_id" data-url="ajax/area" data-query-name="province"></select>
<input type="hidden" class="operate" data-name="city_id" value="=" />
&lt;!&ndash; <select class="area form-control" name="area_id" data-url="ajax/area" data-query-name="city"></select>
<input type="hidden" class="operate" data-name="area_id" value="=" />&ndash;&gt;
</div>
</div>
</div>
</script>-->

View File

@ -130,6 +130,8 @@ class OrderLogic
if($nocancelOrder){
$order = Order::where('id',$dispatch->order_id)->where('status',Order::STATUS_DISPATCHED)->find();
$order->worker_id = 0;
$order->save();
if(empty($order)){
throw new Exception('未找到关联订单');
}

View File

@ -29,7 +29,7 @@ class CheckOrderDispatchCommand extends Command
//1修改为超时
$item->status = OrderDispatch::STATUS_OVERTIME;
$item->save();
$params = ['dispatch'=>$item,'remark'=>'系统自动处理,任务超时'];
$params = ['dispatch'=>$item,'remark'=>'系统自动检测,任务超时'];
Hook::listen('order_dispatch_change',$params);
}
});

View File

@ -175,8 +175,8 @@ class WorkerApi extends BaseService
'time' => Request::instance()->server('REQUEST_TIME'),
'data' => $data,
];
// 如果未设置类型则自动判断
$type = $type ? $type : ($this->request->param(config('var_jsonp_handler')) ? 'jsonp' : $this->responseType);
// 如果未设置类型则使用默认类型判断
$type = $type ? : $this->responseType;
if (isset($header['statuscode'])) {
$code = $header['statuscode'];

View File

@ -214,6 +214,9 @@ return [
// 是否自动开启 SESSION
'auto_start' => true,
//有效期
'expire' => 3600*24,
'type' => 'redis',
'host' => Env::get('redis.redis_host'), // Redis 服务器地址
'port' => Env::get('redis.redis_port'), // Redis 端口

View File

@ -0,0 +1,5 @@
<?php
return [
'baidu_app_key' => 'UQjHtbQQoDQRpAE5QLGFvC8exM6rcu69'
];

View File

@ -6,6 +6,8 @@ use app\common\controller\WorkerApi;
class Common extends WorkerApi
{
protected $noNeedLogin = ['config'];
public function ossParams()
{
$name = $this->request->post('name');
@ -22,4 +24,24 @@ class Common extends WorkerApi
$this->success('获取成功', $params);
}
public function config()
{
$res = [
'user_agreement' => $this->getUserAgreement(),
'privacy_policy' => $this->getPrivacyPolicy(),
];
$this->success('请求成功', $res);
}
private function getUserAgreement(): string
{
return '<span style="box-sizing: border-box;"><strong style="box-sizing: border-box;" >超邦手用户服务协议</strong></span><br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/><span style="color: var(--summaryColor); box-sizing: border-box;">生效日期: 2025年3月3日</span><br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/><span style="font-size: 14px; box-sizing: border-box;">欢迎您使用“超邦手”小程序!本协议由四川亿灵匠网络科技有限公司(以下简称“我们”或“平台”)与您(以下简称“用户”)签订,适用于您在超邦手小程序上的所有活动。<br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/><strong style="box-sizing: border-box;">一、服务内容</strong><br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/>平台服务: 我们为维修师傅和用户提供信息匹配平台。用户可以在平台上发布维修需求,维修师傅可自由选择接单。<br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/>中介性质: 平台仅提供信息发布和撮合服务,不直接参与维修过程。<br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/><strong style="box-sizing: border-box;">二、用户义务</strong><br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/>真实信息: 用户须提供真实有效的信息进行注册,不得冒用他人身份。<br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/>遵守法律: 不得在平台上发布违法信息或从事不正当活动。<br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/>订单履约: 维修师傅须按约定完成服务,不得敷衍了事或拒绝履约。<br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/><strong style="box-sizing: border-box;">三、费用与支付</strong><br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/>支付方式: 用户在下单后通过平台支付维修费用。平台将向师傅支付相应服务费用。<br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/>平台抽佣: 平台会根据订单金额收取一定比例的佣金。<br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/><strong style="box-sizing: border-box;">四、争议解决</strong><br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/>协商处理: 用户与维修师傅之间发生争议,应优先协商解决。<br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/>平台介入: 无法协商一致的,可申请平台介入调解。<br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/><strong style="box-sizing: border-box;">五、协议的修改与终止</strong><br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/>修改: 我们有权根据实际情况对本协议进行修订,并通过平台公告的方式通知。<br style="box-sizing: border-box;"/><br style="box-sizing: border-box;"/>终止: 用户违反本协议的,我们有权终止服务。</span>';
}
private function getPrivacyPolicy(): string
{
return '<span style="font-size: 17px;"><strong>超邦手隐私政策</strong></span><span style="font-size:14px;" ><br/><br/><span style="color: var(--summaryColor); font-size: 14px;" mark-selection="">生效日期: 2025年3月3日</span><br/><br/>我们深知个人信息对您的重要性,并承诺依法保护您的隐私。请您在使用“超邦手”小程序之前,仔细阅读本隐私政策。<br/><br/><strong>一、我们如何收集和使用信息</strong><br/><br/>基本信息: 在您注册账号时,我们会收集您的姓名、手机号等信息。<br/><br/>订单信息: 您在使用平台发布需求或接单时,我们会收集订单详情、地址等信息。<br/><br/>设备信息: 为保障您的账号安全,我们会收集设备型号、操作系统等信息。<br/><br/><strong>二、信息的共享与转让<br/></strong><br/>服务提供: 为促成交易的完成,平台会将必要信息共享给维修师傅。<br/><br/>法律要求: 在法律要求的情况下,我们可能向相关执法部门提供您的信息。<br/><br/><strong>三、信息的存储和保护</strong><br/><br/>存储期限: 您的个人信息将存储在符合安全标准的服务器中,保存期限不超过法律规定的时间。<br/><br/>安全保护: 我们采取严格的安全措施,防止您的信息被非法访问、使用或泄露。<br/><br/><strong>四、您的权利</strong><br/><br/>查询和更正: 您可以在小程序中访问、更正或删除您的个人信息。<br/><br/>撤回同意: 您有权撤回对我们收集和处理您信息的同意。<br/><br/><strong>五、联系我们</strong><br/><br/>如您对本隐私政策有任何疑问,可通过超邦手小程序的客服功能联系我们。<br/><br/><strong>四川亿灵匠网络科技有限公司</strong></span>';
}
}

View File

@ -10,7 +10,6 @@ class Worker extends WorkerApi
/**
* 微信登录
* @return void
*/
public function login()
{

View File

@ -12,9 +12,9 @@ define([], function () {
var zoom_id = $(that).data("zoom-id") ? $(that).data("zoom-id") : "";
var lat = lat_id ? $("#" + lat_id).val() : '';
var lng = lng_id ? $("#" + lng_id).val() : '';
var city_code = $("#area_id").val();
var city_code = $("#c-city").val();
var zoom = zoom_id ? $("#" + zoom_id).val() : '';
var url = "/addons/address/index/select?1=1";
var url = "/addons/address/index/select?a=1";
url += (lat && lng) ? 'lat=' + lat + '&lng=' + lng +
(input_id ? "&address=" + $("#" + input_id).val() : "")
+(zoom ? "&zoom=" + zoom : "") : ''

View File

@ -53,6 +53,8 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
{field: 'images', title: __('Images'), operate: false, events: Table.api.events.image, formatter: Table.api.formatter.images},
{field: 'remark', title: __('Remark'), operate: false, table: table, class: 'autocontent', formatter: Table.api.formatter.content},
{field: 'dispatch_admin_user', title: __('派单员'), operate: '='},
//{field: 'admin_id', title: __('Admin_id')},
{field: 'admin_user', title: __('Admin_user'), operate: '='},
//{field: 'handle_admin_id', title: __('Handle_admin_id')},

View File

@ -29,7 +29,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
columns: [
[
//{field: 'id', title: __('Id')},
{field: 'id', title: __('ID'),visible:false,operate: false},
{field: 'id', title: __('ID'),visible:true,operate: false},
{field: 'admin_user', title: __('派单员'),operate: "LIKE"},
{field: 'count_num', title: __('总订单数'),operate: false},
{field: 'finish_num', title: __('完单数'),operate: false},
@ -45,7 +45,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
{field: 'performance_avg', title: __('客单利润(¥)'), operate: false},
{field: 'total_avg', title: __('客单价(¥)'), operate: false},
{field: 'avg_time_diff', 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},
@ -57,9 +57,27 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
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}
visible:false,
defaultValue: Config.default_daterange
},
{field: 'operate', title: __('Operate'), table: table2, events: Table.api.events.operate, formatter: Table.api.formatter.operate,
buttons: [
{
name: 'aftersales',
text:"售后列表",
title:"售后列表",
icon: 'fa fa-list',
url: function(row){
return 'aftersales/aftersale/index?from=2&man_id='+row.id;
},
extend: 'data-toggle="tooltip" data-container="body"',
classname: 'btn btn-xs btn-default btn-dialog',
visible:function(row){
return true;
}
},
]
}
]
]
});
@ -105,6 +123,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
// ]
// }
const headers = response[0];
const units = ['元', '%', '%', '']; // 如果你有单位可以一起拼接
var barChart = Echarts.init(document.getElementById('bar-chart'), 'walden');
var option = {
legend: {},
@ -112,14 +133,30 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
dataset: {
source: response
},
xAxis: { type: 'category' },
xAxis: {
type: 'category',
axisLabel: {
show: true // 隐藏“系统”
},
axisTick: { show: false },
axisLine: { show: false }
},
yAxis: {},
series: [
{ type: 'bar' },
{ type: 'bar' },
{ type: 'bar' },
{ type: 'bar' }
]
series: headers.slice(1).map((title, i) => ({
type: 'bar',
label: {
show: true,
position: 'top',
formatter: function (params) {
const value = params.value[i + 1]; // 因为第一列是“派单员”
const unit = units[i] || '';
return `${value} ${unit}`; // 显示数值 + 单位
}
},
itemStyle: {
borderRadius: 4
}
}))
};
barChart.setOption(option);
},
@ -186,6 +223,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-t
edit: function () {
Controller.api.bindevent();
},
aftersales: function () {
Controller.api.bindevent();
},
api: {
bindevent: function () {
Form.api.bindevent($("form[role=form]"));

View File

@ -0,0 +1,251 @@
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 () {
//绑定事件
$('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/worker/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:true},
{field: 'name', title: __('姓名'),operate: "="},
{field: 'tel', title: __('电话'),operate: "="},
{field: 'area.name', title: __('城市'),operate: false},
{field: 'star', title: __('评分'),operate: false},
{field: 'performance', title: __('总业绩(¥)'), operate: false},
{field: 'get_count', title: __('接单数'),operate: false},
{field: 'finish_num', title: __('完单数'),operate: false},
{field: 'cost', title: __('收入(¥)'), operate: false},
{field: 'performance_rate', title: __('利润率(%)'), operate: false},
{field: 'trans_rate', title: __('转化率(%)'), operate: false},
{field: 'cash_value', title: __('变现值'), operate: false},
{field: 'refuse_rate', title: __('拒单率(%)'), operate: false},
{field: 'arrive_rate', title: __('上门率(%)'), operate: false},
{field: 'good_rate', title: __('好评率(%)'), operate: false},
//{field: 'refund_total', title: __('退款金额(¥)'), operate: false},
{field: 'refund_count', 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: 'area', title: __(''), searchList: function (column) {
return Template('sourcetpl', {});
}
},
*/
{field: 'area.merge_name', title: __('地区'),operate: 'LIKE',visible:false},
{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,
defaultValue: Config.default_daterange
},
{field: 'operate', title: __('Operate'), table: table2, events: Table.api.events.operate, formatter: Table.api.formatter.operate,
buttons: [
{
name: 'aftersales',
text:"售后列表",
title:"售后列表",
icon: 'fa fa-list',
url: function(row){
return 'aftersales/aftersale/index?from=3&man_id='+row.id;
},
extend: 'data-toggle="tooltip" data-container="body"',
classname: 'btn btn-xs btn-default btn-dialog',
visible:function(row){
return true;
}
},
]
}
]
]
});
// 为表格2绑定事件
Table.api.bindevent(table2);
}
});
// 触发 tab 后发起 ajax 获取图表数据
$('ul.nav-tabs li.active a[data-toggle="tab"]').on("shown.bs.tab", function () {
getChartData();
});
function getChartData(){
return;
// 获取单选框选中的值
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],
// ...
// ]
// }
const headers = response[0];
const units = ['元', '%', '%', '']; // 如果你有单位可以一起拼接
var barChart = Echarts.init(document.getElementById('bar-chart'), 'walden');
var option = {
legend: {},
tooltip: {},
dataset: {
source: response
},
xAxis: {
type: 'category',
axisLabel: {
show: true // 隐藏“系统”
},
axisTick: { show: false },
axisLine: { show: false }
},
yAxis: {},
series: headers.slice(1).map((title, i) => ({
type: 'bar',
label: {
show: true,
position: 'top',
formatter: function (params) {
const value = params.value[i + 1]; // 因为第一列是“派单员”
const unit = units[i] || '';
return `${value} ${unit}`; // 显示数值 + 单位
}
},
itemStyle: {
borderRadius: 4
}
}))
};
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();
},
edit: function () {
Controller.api.bindevent();
},
aftersales: function () {
Controller.api.bindevent();
},
api: {
bindevent: function () {
Form.api.bindevent($("form[role=form]"));
}
}
};
return Controller;
});