From d0938fc8040118c92fc726a4a8b04e5f78de82f8 Mon Sep 17 00:00:00 2001 From: hant Date: Sun, 15 Jun 2025 18:34:41 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/admin/controller/Message.php | 29 +++ application/admin/controller/Order.php | 30 ++- .../admin/controller/orders/Dispatch2.php | 197 +++++++++++++++++- application/admin/view/message/index.html | 12 +- application/admin/view/order/index.html | 1 + .../admin/view/orders/dispatch2/edit.html | 127 +++++------ .../admin/view/orders/dispatch2/index.html | 2 +- public/assets/js/backend/message.js | 14 ++ public/assets/js/backend/order.js | 47 +++-- public/assets/js/backend/orders/dispatch2.js | 48 ++++- 10 files changed, 406 insertions(+), 101 deletions(-) diff --git a/application/admin/controller/Message.php b/application/admin/controller/Message.php index f3eb501..a96235f 100644 --- a/application/admin/controller/Message.php +++ b/application/admin/controller/Message.php @@ -148,4 +148,33 @@ class Message extends Backend } + public function markallread(){ + $list = $this->model + ->where('type',1) + ->where('to_id',$this->auth->id) + //->auth($this->auth) + //->whereRaw("FIND_IN_SET(?, read_uid) = 0 OR read_uid IS NULL OR read_uid = ''", [$this->auth->id]) + ->whereRaw("FIND_IN_SET({$this->auth->id}, read_uid) = 0 OR read_uid = '' or read_uid is null") + ->whereTime('create_time', '>=', '-3 days') + ->select(); + $update = []; + foreach ($list as &$item){ + $readUids = explode(',',$item->read_uid??''); + $item->status = 1; + if(!in_array($this->auth->id,$readUids)){ + $readUids[] = $this->auth->id; + $update[] = [ + 'id' => $item->id, + 'read_uid' => implode(',',$readUids), + 'update_time' => date('Y-m-d H:i:s') + ]; + $item->status = 0; + } + } + if($update){ + $this->batchUpdateByIdSimple('fa_message',$update); + } + $this->success("全部已读"); + } + } diff --git a/application/admin/controller/Order.php b/application/admin/controller/Order.php index 5a7d55c..75f2511 100644 --- a/application/admin/controller/Order.php +++ b/application/admin/controller/Order.php @@ -13,6 +13,8 @@ use app\admin\model\Worker; use app\admin\model\WorkerItem; use app\common\controller\Backend; use app\common\Logic\OrderLogic; +use Carbon\Carbon; +use Carbon\Traits\Creator; use fast\Tree; use think\Db; use think\Exception; @@ -146,6 +148,7 @@ class Order extends Backend 'receive_type', 'plan_time', 'coupon_id', + 'create_time', 'total', 'online_amount', 'aftersale_id' @@ -153,18 +156,33 @@ class Order extends Backend ->where($where); $filter = (array)json_decode(input()['filter'] ?? '', true); $admin_filter = $filter['user.nickname'] ?? false; - + $is_timeout = $filter['is_timeout'] ?? null; +// dd($create_time,$filter); if ($admin_filter) { $admin_ids = Admin::where('nickname', 'like', '%' . $admin_filter . '%')->column('id'); $build->whereIn('admin_id', $admin_ids); } + if (!is_null($is_timeout)) { + if ($is_timeout == 1){ + $build->where('status', '>=', \app\admin\model\Order::STATUS_DISPATCHING) + ->where('create_time', '<=', (new Carbon())->subMinutes(20) + ->format('Y-m-d H:i:s')); + }else{ + $build->where('status', '>=', \app\admin\model\Order::STATUS_DISPATCHING) + ->where('create_time', '>', (new Carbon())->subMinutes(20) + ->format('Y-m-d H:i:s')); + } + + } if ($type == 1) { $build->where('status', '>=', 0); - } - - if ($type == 2) { + }elseif ($type == 2) { $build->where('status', '<', 0); + }elseif ($type == 3){ + $build->where('status', '>=', \app\admin\model\Order::STATUS_DISPATCHING) + ->where('create_time', '<=', (new Carbon())->subMinutes(20) + ->format('Y-m-d H:i:s')); } if ($group == 2 || $group == 6) { // 生成 SQL 语句 @@ -449,7 +467,7 @@ class Order extends Backend $insert [] = [ 'to_id' => $re, 'type' => 1, - 'title' => '订单报错通知', + 'title' => '催单通知', 'content' => '【催单通知】您有一条订单号为' . $order->order_no . '的订单被催单,请立即派单!' ]; } @@ -823,7 +841,7 @@ class Order extends Backend $index = 0; foreach ($filter as $k => $v) { - if ($k == 'user.nickname') continue; + if ($k == 'user.nickname' || $k =='is_timeout') continue; if (!preg_match('/^[a-zA-Z0-9_\-\.]+$/', $k)) { continue; } diff --git a/application/admin/controller/orders/Dispatch2.php b/application/admin/controller/orders/Dispatch2.php index 36198f9..ddc1b46 100644 --- a/application/admin/controller/orders/Dispatch2.php +++ b/application/admin/controller/orders/Dispatch2.php @@ -16,6 +16,7 @@ use think\exception\DbException; use think\exception\PDOException; use think\exception\ValidateException; use think\Hook; +use think\Loader; /** * 派单列管理 @@ -68,12 +69,16 @@ class Dispatch2 extends Backend return $this->selectpage(); } list($where, $sort, $order, $offset, $limit) = $this->buildparams(); +// $filter = (array)json_decode(input()['filter'] ?? '', true); +// $type = $filter['dispatch_type'] ?? false; +// dd($where,$filter); $list = $this->model ->with(['orderb','lastRecord']) ->auth($this->auth) - ->where($where) - ->order($sort, $order) + ->where($where); + + $list = $list->order($sort, $order) ->paginate($limit); foreach ($list as &$row) { @@ -488,4 +493,192 @@ class Dispatch2 extends Backend $this->success(__('取消成功')); } + + /** + * 生成查询所需要的条件,排序方式 + * @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); + $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; + if (!isset($filter['type'])){ + $filter['type'] = 1; + } + 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]; + } + } diff --git a/application/admin/view/message/index.html b/application/admin/view/message/index.html index 3468ab4..02174ee 100644 --- a/application/admin/view/message/index.html +++ b/application/admin/view/message/index.html @@ -7,11 +7,13 @@
  • 正常订单
  • 取消订单
  • +
  • 超时订单
  • diff --git a/application/admin/view/orders/dispatch2/edit.html b/application/admin/view/orders/dispatch2/edit.html index e56a747..0f6e174 100644 --- a/application/admin/view/orders/dispatch2/edit.html +++ b/application/admin/view/orders/dispatch2/edit.html @@ -16,40 +16,40 @@ -
    - -
    - -
    -
    + + + + + + -
    - -
    - -
    -
    + + + + + + -
    - -
    - -
    -
    + + + + + + -
    - -
    - -
    -
    + + + + + + -
    - -
    - -
    -
    + + + + + + {notempty name ='action'} @@ -69,12 +69,15 @@ -
    - -
    - -
    -
    + + + + + + + + + @@ -135,7 +138,7 @@
    - +
    @@ -148,8 +151,29 @@ {/if} +
    + +
    + +
    +
    +
    + +
    +
    + +
    + + +
    + +
    +
      +
      +
      +
      @@ -166,12 +190,7 @@
      -
      - -
      - -
      -
      +
      @@ -193,31 +212,13 @@
      -
      - +
      - +
      - -
      - -
      -
      - -
      - - -
      - -
      -
        -
        -
        - - - - - - -
        @@ -108,4 +107,42 @@ \ No newline at end of file + + \ No newline at end of file diff --git a/application/admin/view/workers/worker/edit.html b/application/admin/view/workers/worker/edit.html index e01c404..18372fc 100644 --- a/application/admin/view/workers/worker/edit.html +++ b/application/admin/view/workers/worker/edit.html @@ -29,20 +29,21 @@
        -
        - -
        - - -
        -
        -
        - + +
          + +
          @@ -122,4 +123,42 @@ \ No newline at end of file + + \ No newline at end of file diff --git a/public/assets/js/backend/order.js b/public/assets/js/backend/order.js index 9c8de83..9286b0d 100644 --- a/public/assets/js/backend/order.js +++ b/public/assets/js/backend/order.js @@ -293,6 +293,24 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function table.bootstrapTable('refresh'); } }, + { + name: "dispatch", + text: "派单", + title: "派单", + extend: 'data-toggle="tooltip" data-container="body"', + classname: 'btn btn-xs btn-info btn-dialog', + icon: 'fa fa-add', + url: function (row) { + return 'orders/dispatch/add?order_id=' + row.id; + }, + visible: function (row) { + if (row.status == 10) { + return true; + } + return false; + }, + refresh: true, + }, { name: "reminder", text: "催单", @@ -425,9 +443,22 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function table.bootstrapTable('refresh', {}); return false; }); - const timer = setInterval(function () { - table.bootstrapTable('refresh', {}); - }, 1000 * 120); + let idleTimer = null; + + function resetIdleTimer() { + clearTimeout(idleTimer); + idleTimer = setTimeout(function () { + // 30秒无操作后执行刷新 + table.bootstrapTable('refresh', {}); + }, 30 * 1000); // 30 秒 + } + + ['mousemove', 'keydown', 'click', 'scroll'].forEach(function (event) { + document.addEventListener(event, resetIdleTimer, false); + }); + + resetIdleTimer(); + }, add: function () { $("#mybuttom").on("click", function () { @@ -625,7 +656,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function $('#item_id_value').val(data.value); } }); - $('#item_id').val($('#item_id').data('value')).focus(); + $('#item_id').val($('#item_id').data('value')); const mainSelect = document.getElementById('receive_type'); const otherSelect = document.getElementById('coupon'); @@ -730,7 +761,6 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function } } - function handleInput(e) { selectedIndex = -1; addressSelected = false; @@ -768,7 +798,6 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function }; } - $('#suggestionList').on('mousedown', 'li', function (e) { const name = $(this).data('name'); const location = $(this).data('location'); // "经度,纬度" diff --git a/public/assets/js/backend/orders/dispatch.js b/public/assets/js/backend/orders/dispatch.js index b7c65b8..7747e2f 100644 --- a/public/assets/js/backend/orders/dispatch.js +++ b/public/assets/js/backend/orders/dispatch.js @@ -320,10 +320,8 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'layer', 'cascader'], $("#reset_btn").on("click", function () { $("#c-city-search").citypicker('reset'); $("#area_id").val(''); - $("#test").val(''); - $("#test").data('myvalue', ''); + $("#item_id").data('myvalue', ''); $("#keyword").val(''); - $('#item_id').zdCascader.reload(_data,true); table.bootstrapTable('refresh', { url: $.fn.bootstrapTable.defaults.extend.index_url + '?' + getQueryData(), }); @@ -336,27 +334,24 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'layer', 'cascader'], $('#c-worker_id').val(worker.id).trigger('input').trigger('change'); }); $('#add_worker').on('click',function (){ - console.log(1); Fast.api.open('workers/worker/add?type=2', '添加工人',{ callback: function (value) { - console.log(2222,value); + table.bootstrapTable('refresh', { + url: $.fn.bootstrapTable.defaults.extend.index_url + '?' + getQueryData(), + }); } }); }); - - $('#item_id').zdCascader({ data: _data, onChange: function ($this, data, allPathData) { - // console.log(data,allPathData); $('#item_id').data('myvalue', data.value); }, - defaultValue:$('#item_id_name').val(), - clear:true, - clickParent:true + // clear:true, + // clickParent:true }); - + $('#item_id').val($('#item_id').data('value')); // 为表格绑定事件 Table.api.bindevent(table); diff --git a/public/assets/js/backend/workers/worker.js b/public/assets/js/backend/workers/worker.js index b1727cf..6184ef1 100644 --- a/public/assets/js/backend/workers/worker.js +++ b/public/assets/js/backend/workers/worker.js @@ -218,11 +218,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree'], Form.events.citypicker($("form")); }, add: function () { - $("#c-city").on("cp:updated", function () { - var citypicker = $(this).data("citypicker"); - var code = citypicker.getCode("district") || citypicker.getCode("city") || citypicker.getCode("province"); - $("#area_id").val(code); - }); + const urlParams = new URLSearchParams(window.location.search); const type = urlParams.get('type'); if (type) { @@ -232,14 +228,12 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree'], } } Controller.api.bindevent(); + Controller.api.area(); }, edit: function () { - $("#c-city").on("cp:updated", function () { - var citypicker = $(this).data("citypicker"); - var code = citypicker.getCode("district") || citypicker.getCode("city") || citypicker.getCode("province"); - $("#area_id").val(code); - }); + Controller.api.bindevent(); + Controller.api.area(); }, api: { bindevent: function () { @@ -290,6 +284,152 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree'], "data": content } }); + }, + + area: function (){ + const amapKey = 'c299da50c080dfccf9b1d00560ff9639'; + + let isComposing = false; + let addressSelected = false; + let selectedIndex = -1; + $('#c-address') + .on('compositionstart', () => { + isComposing = true; + }) + .on('compositionend', () => { + isComposing = false; + // 不执行 handleInput,由 input 触发 + }) + .on('input', debounce(function (e) { + if (!isComposing) handleInput(e); + }, 300)) + .on('blur', function () { + // 如果还没有选择地址,且列表中有内容,则默认选择第一个 + + // 如果还没有选择地址,且列表中有内容,则默认选择第一个 + if (!addressSelected && $('#suggestionList li').length > 0) { + choseFirst(); + } + + $('#suggestionList').hide(); + }) + .on('focus', function () { + // 如果还没有选择地址,且列表中有内容,则默认选择第一个 + $('#suggestionList').show(); + }) + .on('keydown', function (e) { + const $items = $('#suggestionList li'); + const len = $items.length; + + if (!len) return; + + if (e.key === 'ArrowDown') { + e.preventDefault(); + selectedIndex = (selectedIndex + 1) % len; + updateActiveItem($items); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + selectedIndex = (selectedIndex - 1 + len) % len; + updateActiveItem($items); + } else if (e.key === 'Enter') { + if (selectedIndex >= 0 && selectedIndex < len) { + e.preventDefault(); + $items.eq(selectedIndex).trigger('mousedown'); + } + } + }); + function updateActiveItem($items) { + $items.removeClass('active'); + if (selectedIndex >= 0) { + const $active = $items.eq(selectedIndex); + $active.addClass('active'); + + // 自动滚动使其可见 + const container = $('#suggestionList')[0]; + const item = $active[0]; + + if (item && container) { + const itemTop = item.offsetTop; + const itemBottom = itemTop + item.offsetHeight; + const containerTop = container.scrollTop; + const containerBottom = containerTop + container.clientHeight; + + if (itemTop < containerTop) { + container.scrollTop = itemTop; + } else if (itemBottom > containerBottom) { + container.scrollTop = itemBottom - container.clientHeight; + } + } + } + } + + function handleInput(e) { + selectedIndex = -1; + addressSelected = false; + const keyword = $(e.target).val(); + if (!keyword.trim() || /^[\s\p{P}]+$/u.test(keyword)) return; + if (!keyword) return $('#c-address').empty(); + + $.getJSON('https://restapi.amap.com/v3/assistant/inputtips', { + key: amapKey, + keywords: keyword, + datatype: 'all', + city: '全国', + }, function (res) { + if (res.tips) { + let html = ''; + res.tips.forEach(tip => { + if (tip.location) { + html += `
        • + ${tip.district} ${tip.name} +
        • `; + } + }); + $('#suggestionList').html(html).show(); + } + }); + } + + function debounce(fn, delay = 300) { + let timer = null; + return function (...args) { + clearTimeout(timer); + timer = setTimeout(() => { + fn.apply(this, args); + }, delay); + }; + } + + $('#suggestionList').on('mousedown', 'li', function (e) { + const name = $(this).data('name'); + const location = $(this).data('location'); // "经度,纬度" + const area_id = $(this).data('area_id'); // "经度,纬度" + const [lng, lat] = location.split(','); + $('#c-address').val(name); + $('#lng').val(lng); + $('#lat').val(lat); + $('#area_id').val(area_id); + // 隐藏提示列表 + $('#suggestionList').hide(); + selectedIndex = -1; + addressSelected = true; + }); + + function choseFirst(){ + const first = $('#suggestionList li').first(); + const name = first.data('name'); + const location = first.data('location'); // "经度,纬度" + const area_id = first.data('area_id'); // "经度,纬度" + const [lng, lat] = location.split(','); + console.log('chose'); + $('#lng').val(lng); + $('#lat').val(lat); + $('#area_id').val(area_id); + // 隐藏提示列表 + $('#suggestionList').hide(); + selectedIndex = -1; + addressSelected = true; + } } } };