This commit is contained in:
todaywindy 2025-06-27 16:02:47 +08:00
parent 865aa90dcb
commit 4c14780e82
12 changed files with 318 additions and 94 deletions

View File

@ -108,7 +108,7 @@ return [
'title' => '上传有效时长',
'type' => 'string',
'content' => [],
'value' => '600',
'value' => '3000',
'rule' => 'required',
'msg' => '',
'tip' => '用户停留页面上传有效时长,单位秒',
@ -120,7 +120,7 @@ return [
'title' => '最大可上传',
'type' => 'string',
'content' => [],
'value' => '10M',
'value' => '20M',
'rule' => 'required',
'msg' => '',
'tip' => '',

View File

@ -137,7 +137,7 @@ class Item extends Backend
$params['level'] = $parent->value('level') + 1;
}
}else{
$params['level'] = 0;
$params['level'] = 1;
}

View File

@ -202,9 +202,9 @@ class Order extends Backend
}
if ($group == 2 ) {
if ($group == 2 ) { // 录单员
$build->where('admin_id',$user->id);
}elseif ($group == 6){
}elseif ($group == 6){ // 派单员
// 生成 SQL 语句
$ids = $user->area_ids ?? '';
if ($ids == '') {

View File

@ -13,6 +13,7 @@ use fast\Tree;
use think\Db;
use think\exception\PDOException;
use think\exception\ValidateException;
use think\Loader;
/**
* 师傅列管理
@ -27,7 +28,8 @@ class Worker extends Backend
* Worker模型对象
* @var \app\admin\model\Worker
*/
protected $model = null,$items;
protected $model = null;
protected $items;
public function _initialize()
{
@ -74,8 +76,18 @@ class Worker extends Backend
{
//当前是否为关联查询
$this->relationSearch = true;
$this->assignconfig('permissions', [
'export' => !empty(array_intersect($this->auth->getGroupIds(),[1,11])),
]);
//设置过滤方法
$this->request->filter(['strip_tags', 'trim']);
$items = $this->items;
$filtered = array_filter($items, function ($item) {
return $item['pid'] == 0;
});
$pid_map = array_column($filtered, null, 'id');
if ($this->request->isAjax()) {
//如果发送的来源是Selectpage则转发到Selectpage
if ($this->request->request('keyField')) {
@ -83,7 +95,10 @@ class Worker extends Backend
}
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$area_code = request()->get('area_id');
$item_id = request()->get('item_id');
$filter = (array)json_decode(input()['filter'] ?? '', true);
$area_id = $filter['area_id'] ?? false;
$item_id = $filter['item_id'] ?? false;
$keyword = request()->get('keyword');
$build = $this->model
->with(['area','admin' => function($q){
@ -97,9 +112,16 @@ class Worker extends Backend
$q->where('name', 'like', '%' . $keyword . '%')->whereOr('tel', 'like', '%' . $keyword . '%');
});
}
if ($area_id) {
$build->where('area_id', 'like', $this->getSelectAreaCode($area_id) . '%');
}
if ($item_id) {
$build->join('worker_item', 'worker_item.worker_id = worker.id', 'left');
$build->where('worker_item.item_id', $item_id);
$item_ids = $this->getItemsById($item_id);
$item_ids [] = $item_id;
$worker_ids = WorkerItem::whereIn('item_id',$item_ids)->column('worker_id');
$build->whereIn('id', $worker_ids);
}
if ($area_code) {
@ -108,32 +130,43 @@ class Worker extends Backend
$list = $build
->paginate($limit);
$data = [];
$worker_ids = [];
foreach ($list->items() as $item){
$dt = $item->toArray();
$dt['tel'] = substr($dt['tel'], 0, 3) . '****' . substr($dt['tel'], -4);
$data[] = $dt;
$worker_ids [] = $item['id'];
}
$worker_item = WorkerItem::whereIn('worker_id',$worker_ids)->where('item_path_id',1)
->field('worker_id,item_id')->select();
$worker_item_map = [];
// dd($worker_item);
foreach ($worker_item as $item){
$worker_item_map[$item->worker_id] [] = $item->item_id;
}
foreach ($data as &$datum){
// dd($worker_item_map);
if (key_exists($datum['id'],$worker_item_map)){
foreach ($worker_item_map[$datum['id']] as $item){
$datum ['worker_item'][] = trim($pid_map[$item]['title']);
}
}else{
$datum ['worker_item'] = [];
}
$datum['worker_item'] = implode(',',$datum['worker_item']);
}
$result = array("total" => $list->total(), "rows" => $list->items());
$result = array("total" => $list->total(), "rows" => $data);
return json($result);
}
$items = Db::name('item')
->where('status', 1)
->field(['id', 'title', 'key_word', 'pid'])
->order('pid', 'asc')
->order('sort', 'desc')
->select();
$filtered = array_filter($items, function ($item) {
return $item['pid'] == 0;
});
$pid_map = array_column($filtered, null, 'id');
$res_items = [];
foreach ($items as $item) {
if ($item['pid'] != 0 && isset($pid_map[$item['pid']])) {
$res_items [] = [
...$item, 'ptitle' => $pid_map[$item['pid']]['title']
];
}
}
$this->view->assign('items', $res_items);
$tree = $this->buildTree($items);
$formattedTree = $this->formatTree($tree);
$this->view->assign('items', $formattedTree);
return $this->view->fetch();
}
@ -471,4 +504,196 @@ ORDER BY distance;",[$order->lng,$order->lat]);
$this->error(__('No rows were deleted'));
}
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['item_id']);
unset($filter['area_id']);
$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;
// ✅ 新增右匹配like%
case 'LIKE%':
$where[] = [$k, 'LIKE', "{$v}%"];
break;
// ✅ 新增:左匹配(%like
case '%LIKE':
$where[] = [$k, 'LIKE', "%{$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

@ -12,7 +12,7 @@ class Order extends Validate
protected $rule = [
'source' => 'require',
'item_id' => 'require',
'customer' => 'require|max:32',
'customer' => 'max:32',
'tel' => 'require|number|max:32|regex:/^1[3-9]\d{9}$/',
'area_id' => 'require',
'address' => 'require|max:255',
@ -28,7 +28,6 @@ class Order extends Validate
'source.require' => '请选择订单渠道',
'item_id.require' => '请选择服务项目',
'customer.require' => '请输入客户昵称',
'customer.max' => '客户昵称不能超过 32 个字符',
'tel.require' => '请输入客户电话',

View File

@ -24,7 +24,7 @@
</div>
<div class="line flex-sb">
<div class="item flex-sb">
<div class="title flex-c"><span class="required">*</span>客户昵称:</div>
<div class="title flex-c">客户昵称:</div>
<div class="value flex-c">
<input id="c-customer" placeholder="请输入客户昵称" class="form-input form-control" name="row[customer]" type="text">
</div>

View File

@ -24,7 +24,7 @@
</div>
<div class="line flex-sb">
<div class="item flex-sb">
<div class="title flex-c"><span class="required">*</span>客户昵称:</div>
<div class="title flex-c">客户昵称:</div>
<div class="value flex-c">
<input id="c-customer" placeholder="请输入客户昵称" value="{$row['customer']}" class="form-control form-input" name="row[customer]" type="text">
</div>

View File

@ -23,7 +23,7 @@
</div>
<div class="line flex-sb">
<div class="item flex-sb">
<div class="title flex-c"><span class="required">*</span>客户昵称:</div>
<div class="title flex-c">客户昵称:</div>
<div class="value flex-c">
<input id="c-customer" placeholder="请输入客户昵称" value="{$row['customer']}" class="form-control form-input" name="row[customer]" type="text">
</div>

View File

@ -1,3 +1,5 @@
<link rel="stylesheet" href="/assets/css/select.css">
<div class="panel panel-default panel-intro">
<div class="panel-heading">
@ -39,36 +41,6 @@
<!-- <a class="btn btn-success btn-recyclebin btn-dialog {:$auth->check('workers/worker/recyclebin')?'':'hide'}" href="workers/worker/recyclebin" title="{:__('Recycle bin')}"><i class="fa fa-recycle"></i> {:__('Recycle bin')}</a>-->
<form id="select-form" style="padding-left: 15px;margin-top: 10px" role="form" class="form-horizontal" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label class="col-xs-12 col-sm-3" style="padding-left: 0px;text-align: left">区域:</label>
<div style="display: inline-block;width: 300px;position: absolute">
<input id="c-city-search" data-rule="required" class="form-control" data-toggle="city-picker" type="text" />
<input id="area_id" style="display: none" class="form-control" name="area_id" hidden type="text" />
</div>
</div>
<!-- <div class="form-group">-->
<!-- <label class="col-xs-12 col-sm-2" style="text-align: left;padding-left: 0px;">工种:</label>-->
<!-- <div style="width: 300px;display: inline-block;">-->
<!-- <select id="citem" data-live-search="true" title="请选择" name="item_id" class="form-control selectpicker show-tick">-->
<!-- <option value="0">不过滤</option>-->
<!-- {foreach $items as $item}-->
<!-- <option value="{$item['id']}">{$item['title']}</option>-->
<!-- {/foreach}-->
<!-- </select>-->
<!-- </div>-->
<!-- </div>-->
<div class="form-group">
<label class="col-xs-12 col-sm-3" style="padding-left: 0px;text-align: left">关键字:</label>
<div style="display: inline-block;width: 300px;position: absolute">
<input id="keyword" class="form-control" style="width: 100%;height: 32px" placeholder="名称或电话号码搜索" name="keyword" type="text" value="" />
</div>
</div>
<p id="search_btn" class="btn btn-primary">搜索</p>
<p id="reset_btn" class="btn btn-primary">清空</p>
</form>
</div>
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-edit="{:$auth->check('workers/worker/edit')}"
@ -81,3 +53,24 @@
</div>
</div>
</div>
<script>
var items = {:json_encode($items); };
</script>
<script id="categorytpl" type="text/html">
<div class="row">
<div class="col-xs-12">
<input id="select_city" class="form-control" data-toggle="city-picker" type="text" />
<input id="select_area_id" type="hidden" name="area_id" />
</div>
</div>
</script>
<script id="items_picker" type="text/html">
<div class="row">
<div class="col-xs-12">
<input id="select_item" class="form-control" type="text" autocomplete="off" />
<input id="select_item_id" type="hidden" name="item_id" />
</div>
</div>
</script>

View File

@ -681,7 +681,7 @@ class Backend extends Controller
foreach ($tree as $item) {
$formattedItem = [
'value' => $item['id'],
'label' => $item['title'],
'label' => str_replace('&nbsp;', '', $item['title'])
];
if (isset($item['children']) && count($item['children']) > 0) {

View File

@ -90,7 +90,7 @@ ${data.receive_type == 1 ? '已收定金' : '已收全款'}
pk: 'id',
sortName: 'id',
fixedRightNumber: 1,
fixedNumber: 3,
fixedNumber: 4,
fixedColumns: true,
renderDefault: true,
searchFormVisible: true,
@ -98,6 +98,7 @@ ${data.receive_type == 1 ? '已收定金' : '已收全款'}
columns: [
[
{checkbox: true},
{field: 'order_no', title: __('Order_no'), operate: 'LIKE'},
{field: 'id', title: __('Id'),searchable: false},
{
field: 'status',
@ -163,9 +164,8 @@ ${data.receive_type == 1 ? '已收定金' : '已收全款'}
{field: 'item_title', title: __('Item_title'), operate: false},
{field: 'user.nickname', title: '录单员',operate: false},
{field: 'order_no', title: __('Order_no'), operate: 'LIKE'},
{field: 'customer', title: __('Customer'), operate: false},
{field: 'user.nickname', title: '录单员',operate: 'LIKE'},
{field: 'customer', title: __('Customer'), operate: 'LIKE'},
{field: 'tel', title: __('Tel'), operate: 'LIKE'},
{field: 'area.merge_name', title: __('Area_id'), searchable: false},
{
@ -223,7 +223,7 @@ ${data.receive_type == 1 ? '已收定金' : '已收全款'}
{
field: 'create_time',
title: __('Create_time'),
operate: false,
operate: 'RANGE',
addclass: 'datetimerange',
autocomplete: false
},

View File

@ -1,4 +1,4 @@
define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree'],
define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree','cascader'],
function ($, undefined, Backend, Table, Form) {
$.jstree.core.prototype.get_all_checked = function (full) {
var obj = this.get_selected(), i, j;
@ -38,6 +38,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree'],
pk: 'id',
sortName: 'id',
fixedColumns: true,
showExport:Config.permissions.export,
fixedRightNumber: 1,
columns: [
[
@ -56,6 +57,10 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree'],
searchList: {"1": __('Status 1'), "0": __('Status 0')},
formatter: Table.api.formatter.status
},
{
field: 'worker_item',
title: '工种',
},
//{field: 'area_id', title: __('Area_id')},
//{field: 'lng', title: __('Lng'), operate:'BETWEEN'},
//{field: 'lat', title: __('Lat'), operate:'BETWEEN'},
@ -66,6 +71,20 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree'],
{field: 'remark', title: '备注'},
{field: 'deposit_amount', title: __('Deposit_amount'), operate: 'BETWEEN'},
{field: 'star', title: __('Star'), operate: 'BETWEEN'},
{
field: 'area_id', title: __('地区'), searchList: function (column) {
return Template('categorytpl', {});
}, formatter: function (value, row, index) {
return '无';
}, visible: false
},
{
field: 'item_id', title: '服务项目', searchList: function (column) {
return Template('items_picker', {});
}, formatter: function (value, row, index) {
return '无';
}, visible: false
},
{
field: '收款码',
title: __('Images'),
@ -113,33 +132,23 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree'],
]
]
});
$("#c-city-search").on("cp:updated", function () {
$("#select_city").on("cp:updated", function () {
var citypicker = $(this).data("citypicker");
var code = citypicker.getCode("district") || citypicker.getCode("city") || citypicker.getCode("province");
// table.bootstrapTable('refresh',{query: {area_code: code}});
$('#area_id').val(code);
});
$("#search_btn").on("click", function () {
table.bootstrapTable('refresh', {
url: $.fn.bootstrapTable.defaults.extend.index_url + '&' + getQueryData(),
});
$("#select_area_id").val(code);
});
function getQueryData() {
return $('#select-form').serialize();
}
$("#reset_btn").on("click", function () {
$("#c-city-search").citypicker('reset');
$("#area_id").val('');
$("#citem").val('').selectpicker('refresh');
$("#keyword").val('');
table.bootstrapTable('refresh', {
url: $.fn.bootstrapTable.defaults.extend.index_url + '?' + getQueryData(),
});
var _data = items;
$('#select_item').zdCascader({
data: _data,
onChange: function ($this, data, allPathData) {
// console.log(data,allPathData);
$('#select_item_id').val(data.value);
},
clear: true,
clickParent: true
});
Form.events.citypicker($("#select-form"));
// 批量修改状态事件
$(document).on("click", ".btn-change-status-off", function () {
@ -212,10 +221,8 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree'],
});
});
// 为表格绑定事件
Table.api.bindevent(table);
Form.events.citypicker($("form"));
},
add: function () {