优化后台拖拽排序逻辑

pull/515/head
Karson 2025-06-03 11:19:01 +08:00
parent fad64c36f0
commit e2901f3b5e
9 changed files with 173 additions and 71 deletions

View File

@ -50,10 +50,9 @@ class Ajax extends Backend
$controllername = $this->request->get('controllername');
$lang = $this->request->get('lang');
if (!$lang || !in_array($lang, config('allow_lang_list')) || !$controllername || !preg_match("/^[a-z0-9_\.]+$/i", $controllername)) {
return jsonp(['errmsg' => '参数错误'], 200, [], ['json_encode_param' => JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE]);
return jsonp(['errmsg' => __('Invalid parameters')], 200, [], ['json_encode_param' => JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE]);
}
$controllername = input("controllername");
$className = Loader::parseClass($this->request->module(), 'controller', $controllername, false);
//存在对应的类才加载
@ -135,66 +134,26 @@ class Ajax extends Backend
*/
public function weigh()
{
//排序的数组
$ids = $this->request->post("ids");
//拖动的记录ID
$changeid = $this->request->post("changeid");
//操作字段
$field = $this->request->post("field");
//操作的数据表
$table = $this->request->post("table");
if (!Validate::is($table, "alphaDash")) {
$this->error();
if (!$this->request->isPost()) {
$this->error(__('Invalid request'));
}
//主键
$pk = $this->request->post("pk");
//排序的方式
$orderway = strtolower($this->request->post("orderway", ""));
$orderway = $orderway == 'asc' ? 'ASC' : 'DESC';
$sour = $weighdata = [];
$ids = explode(',', $ids);
$prikey = $pk && preg_match("/^[a-z0-9\-_]+$/i", $pk) ? $pk : (Db::name($table)->getPk() ?: 'id');
$pid = $this->request->post("pid", "");
//限制更新的字段
$field = in_array($field, ['weigh']) ? $field : 'weigh';
// 如果设定了pid的值,此时只匹配满足条件的ID,其它忽略
if ($pid !== '') {
$hasids = [];
$list = Db::name($table)->where($prikey, 'in', $ids)->where('pid', 'in', $pid)->field("{$prikey},pid")->select();
foreach ($list as $k => $v) {
$hasids[] = $v[$prikey];
}
$ids = array_values(array_intersect($ids, $hasids));
$controllername = $this->request->post('controllername');
if (!$controllername || !preg_match("/^[a-z0-9_\.]+$/i", $controllername)) {
$this->error(__('Invalid parameters'));
}
if (!$this->auth->check(str_replace('.', '/', $controllername) . '/dragsort')) {
$this->error(__('You have no permission'));
}
$list = Db::name($table)->field("$prikey,$field")->where($prikey, 'in', $ids)->order($field, $orderway)->select();
foreach ($list as $k => $v) {
$sour[] = $v[$prikey];
$weighdata[$v[$prikey]] = $v[$field];
$className = Loader::parseClass($this->request->module(), 'controller', $controllername, false);
if (!class_exists($className)) {
$this->error(__('Invalid parameters'));
}
$position = array_search($changeid, $ids);
$desc_id = $sour[$position] ?? end($sour); //移动到目标的ID值,取出所处改变前位置的值
$sour_id = $changeid;
$weighids = [];
$temp = array_values(array_diff_assoc($ids, $sour));
foreach ($temp as $m => $n) {
if ($n == $sour_id) {
$offset = $desc_id;
} else {
if ($sour_id == $temp[0]) {
$offset = $temp[$m + 1] ?? $sour_id;
} else {
$offset = $temp[$m - 1] ?? $sour_id;
}
}
if (!isset($weighdata[$offset])) {
continue;
}
$weighids[$n] = $weighdata[$offset];
Db::name($table)->where($prikey, $n)->update([$field => $weighdata[$offset]]);
}
$this->success();
// 重设action方法中获取控制器名称
$this->request->controller($controllername);
return action('admin/' . $controllername . '/dragsort');
}
/**
@ -213,21 +172,21 @@ class Ajax extends Backend
if ($type == 'content') {
break;
}
// no break
// no break
case 'template':
// 模板缓存
rmdirs(TEMP_PATH, false);
if ($type == 'template') {
break;
}
// no break
// no break
case 'addons':
// 插件缓存
Service::refresh();
if ($type == 'addons') {
break;
}
// no break
// no break
case 'browser':
// 浏览器缓存
// 只有生产环境下才修改
@ -288,8 +247,8 @@ class Ajax extends Backend
{
$params = $this->request->get("row/a");
if (!empty($params)) {
$province = isset($params['province']) ? $params['province'] : null;
$city = isset($params['city']) ? $params['city'] : null;
$province = $params['province'] ?? null;
$city = $params['city'] ?? null;
} else {
$province = $this->request->get('province');
$city = $this->request->get('city');

View File

@ -6,6 +6,8 @@ use app\admin\model\AuthRule;
use app\common\controller\Backend;
use fast\Tree;
use think\Cache;
use think\db\Query;
use think\exception\HttpResponseException;
/**
* 规则管理
@ -31,7 +33,7 @@ class Rule extends Backend
}
$this->model = model('AuthRule');
// 必须将结果集转换为数组
$ruleList = \think\Db::name("auth_rule")->field('type,condition,remark,createtime,updatetime', true)->order('weigh DESC,id ASC')->select();
$ruleList = \think\Db::name("auth_rule")->field('type,condition,remark,menutype,extend,pinyin,py,createtime,updatetime', true)->order('weigh DESC,id ASC')->select();
foreach ($ruleList as $k => &$v) {
$v['title'] = __($v['title']);
}
@ -154,4 +156,18 @@ class Rule extends Backend
}
$this->error();
}
/**
* 排序
*/
public function dragsort()
{
// 注册Hook
\think\Hook::add('admin_dragsort_after', function ($model) {
Cache::rm('__menu__');
});
parent::dragsort();
}
}

View File

@ -15,7 +15,9 @@ use think\db\exception\ModelNotFoundException;
use think\exception\DbException;
use think\exception\PDOException;
use think\exception\ValidateException;
use think\Hook;
use think\response\Json;
use think\Validate;
trait Backend
{
@ -32,7 +34,7 @@ trait Backend
unset($params[$field]);
}
}
} else if (array_key_exists($this->excludeFields, $params)) {
} elseif (array_key_exists($this->excludeFields, $params)) {
unset($params[$this->excludeFields]);
}
return $params;
@ -341,6 +343,128 @@ trait Backend
$this->error(__('No rows were updated'));
}
/**
* 排序
*
* @return void
*/
public function dragsort()
{
$idsString = $this->request->post("ids");
$changeid = $this->request->post("changeid");
$field = $this->request->post("field", "weigh");
$pk = $this->request->post("pk");
$pid = $this->request->post("pid", "");
$orderway = $this->request->post("orderway", "desc");
// 检测参数是否有效
if (empty($idsString) || empty($changeid)) {
$this->error(__('Invalid parameters'));
}
// 验证排序方式
$orderway = strtolower($orderway);
if (!in_array($orderway, ['asc', 'desc'])) {
$orderway = 'desc';
}
// 获取模型表名
$table = $this->model->getTable();
$ids = explode(',', $idsString);
// 验证changeid是否在ids中
if (!in_array($changeid, $ids)) {
$this->error(__('Invalid parameters'));
}
// 允许的排序字段白名单
$allowedFields = explode(',', $this->dragsortFields);
$field = in_array($field, $allowedFields) ? $field : '';
if (!$field) {
$this->error(__('You have no permission'));
}
// 获取主键
$prikey = $this->model->getPk();
// 开始事务
Db::startTrans();
try {
// 处理pid筛选
if ($pid !== '') {
$pidValues = explode(',', $pid); // 支持多个pid值
$validRecords = Db::table($table)->where($prikey, 'in', $ids)->where('pid', 'in', $pidValues)->column($prikey);
// 只保留有效的ID
$ids = array_values(array_intersect($ids, $validRecords));
if (empty($ids)) {
Db::rollback();
exception(__('No rows were updated'));
}
}
// 获取当前记录的权重值
$currentWeights = Db::table($table)->where($prikey, 'in', $ids)->column($field, $prikey);
// 直接从$currentWeights获取最大和最小权重值
$minWeight = !empty($currentWeights) ? min($currentWeights) : 1;
$maxWeight = !empty($currentWeights) ? max($currentWeights) : count($ids);
// 计算权重步长
$step = 1;
// 准备批量更新数据 - 直接根据新顺序分配权重
$updateData = [];
// 根据排序方式设置初始权重和步长方向
if ($orderway == 'asc') {
// 升序:从小到大排列
$newWeight = $minWeight;
$step = abs($step);
} else {
// 降序:从大到小排列
$newWeight = $maxWeight;
$step = -abs($step);
}
// 根据新的顺序分配权重值
foreach ($ids as $index => $id) {
// 计算该记录应有的新权重值
$expectedWeight = $newWeight;
$newWeight += $step;
// 如果当前权重与期望权重不同,才需要更新
if (!isset($currentWeights[$id]) || $currentWeights[$id] != $expectedWeight) {
$updateData[] = [$prikey => $id, $field => $expectedWeight];
}
}
// 批量更新
if (!empty($updateData)) {
foreach ($updateData as $data) {
Db::table($table)->where($prikey, $data[$prikey])->update([$field => $data[$field]]);
}
}
// 提交事务
Db::commit();
} catch (\Exception $e) {
// 发生异常时回滚事务
Db::rollback();
$this->error();
}
Hook::listen("admin_dragsort_after", $this->model);
$this->success();
}
/**
* 导入
*
@ -391,7 +515,7 @@ trait Backend
}
//导入文件首行类型,默认是注释,如果需要使用字段名称请使用name
$importHeadType = isset($this->importHeadType) ? $this->importHeadType : 'comment';
$importHeadType = $this->importHeadType ?? 'comment';
$table = $this->model->getQuery()->getTable();
$database = \think\Config::get('database.database');

View File

@ -25,6 +25,7 @@
<table id="table" class="table table-bordered table-hover"
data-operate-edit="{:$auth->check('auth/rule/edit')}"
data-operate-del="{:$auth->check('auth/rule/del')}"
data-operate-dragsort="{:$auth->check('auth/rule/dragsort')}"
width="100%">
</table>
</div>

View File

@ -26,6 +26,7 @@
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-edit="{:$auth->check('category/edit')}"
data-operate-del="{:$auth->check('category/del')}"
data-operate-dragsort="{:$auth->check('category/dragsort')}"
width="100%">
</table>
</div>

View File

@ -19,6 +19,7 @@
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-edit="{:$auth->check('user/rule/edit')}"
data-operate-del="{:$auth->check('user/rule/del')}"
data-operate-dragsort="{:$auth->check('user/rule/dragsort')}"
width="100%">
</table>
</div>

View File

@ -38,7 +38,6 @@ class Ajax extends Frontend
return jsonp(['errmsg' => '参数错误'], 200, [], ['json_encode_param' => JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE]);
}
$controllername = input("controllername");
$className = Loader::parseClass($this->request->module(), 'controller', $controllername, false);
//存在对应的类才加载

View File

@ -10,7 +10,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
edit_url: 'category/edit',
del_url: 'category/del',
multi_url: 'category/multi',
dragsort_url: 'ajax/weigh',
dragsort_url: 'category/dragsort',
table: 'category',
}
});

View File

@ -335,7 +335,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
if ($(".export .exporttips").length === 0) {
$(".export .dropdown-menu").prepend("<li class='exporttips alert alert-warning-light mb-0 no-border p-2'></li>")
}
$(".export .exporttips").html("导出记录:" + (selectedIds.length > 0 ? "选中" : "全部"));
$(".export .exporttips").html("导出记录:" + (selectedIds.length > 0 ? "选中" : "全部"));
}
$(Table.config.disabledbtn, toolbar).toggleClass('disabled', !options.selectedIds.length);
@ -503,8 +503,9 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
pid: pid,
field: Table.config.dragsortfield,
orderway: options.sortOrder,
table: options.extend.table,
pk: options.pk
table: options.extend.table || '',
pk: options.pk,
controllername: Config.controllername || ''
}
};
Fast.api.ajax(params, function (data, ret) {