Index.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 海豚PHP框架 [ DolphinPHP ]
  4. // +----------------------------------------------------------------------
  5. // | 版权所有 2016~2019 广东卓锐软件有限公司 [ http://www.zrthink.com ]
  6. // +----------------------------------------------------------------------
  7. // | 官方网站: http://dolphinphp.com
  8. // +----------------------------------------------------------------------
  9. namespace app\user\admin;
  10. use app\admin\controller\Admin;
  11. use app\common\builder\ZBuilder;
  12. use app\user\model\User as UserModel;
  13. use app\user\model\Role as RoleModel;
  14. use app\admin\model\Module as ModuleModel;
  15. use app\admin\model\Access as AccessModel;
  16. use util\Tree;
  17. use think\Db;
  18. use think\facade\Hook;
  19. /**
  20. * 用户默认控制器
  21. * @package app\user\admin
  22. */
  23. class Index extends Admin
  24. {
  25. /**
  26. * 用户首页
  27. * @author 蔡伟明 <314013107@qq.com>
  28. * @return mixed
  29. * @throws \think\Exception
  30. * @throws \think\exception\DbException
  31. */
  32. public function index()
  33. {
  34. cookie('__forward__', $_SERVER['REQUEST_URI']);
  35. // 获取查询条件
  36. $map = $this->getMap();
  37. // 非超级管理员检查可管理角色
  38. if (session('user_auth.role') != 1) {
  39. $role_list = RoleModel::getChildsId(session('user_auth.role'));
  40. $map[] = ['role', 'in', $role_list];
  41. }
  42. // 数据列表
  43. $data_list = UserModel::where($map)->order('sort,role,id desc')->paginate();
  44. // 授权按钮
  45. $btn_access = [
  46. 'title' => '授权',
  47. 'icon' => 'fa fa-fw fa-key',
  48. 'href' => url('access', ['uid' => '__id__'])
  49. ];
  50. // 角色列表
  51. if (session('user_auth.role') != 1) {
  52. $role_list = RoleModel::getTree(null, false, session('user_auth.role'));
  53. } else {
  54. $role_list = RoleModel::getTree();
  55. }
  56. // 使用ZBuilder快速创建数据表格
  57. return ZBuilder::make('table')
  58. ->setPageTitle('用户管理') // 设置页面标题
  59. ->setTableName('admin_user') // 设置数据表名
  60. ->setSearch(['id' => 'ID', 'username' => '用户名', 'email' => '邮箱']) // 设置搜索参数
  61. ->addColumns([ // 批量添加列
  62. ['id', 'ID'],
  63. ['username', '用户名'],
  64. ['nickname', '昵称'],
  65. ['role', '角色', $role_list],
  66. ['email', '邮箱'],
  67. ['mobile', '手机号'],
  68. ['create_time', '创建时间', 'datetime'],
  69. ['status', '状态', 'switch'],
  70. ['right_button', '操作', 'btn']
  71. ])
  72. ->addTopButtons('add,enable,disable,delete') // 批量添加顶部按钮
  73. ->addRightButton('custom', $btn_access) // 添加授权按钮
  74. ->addRightButtons('edit,delete') // 批量添加右侧按钮
  75. ->setRowList($data_list) // 设置表格数据
  76. ->fetch(); // 渲染页面
  77. }
  78. /**
  79. * 新增
  80. * @author 蔡伟明 <314013107@qq.com>
  81. * @return mixed
  82. * @throws \think\Exception
  83. */
  84. public function add()
  85. {
  86. // 保存数据
  87. if ($this->request->isPost()) {
  88. $data = $this->request->post();
  89. // 验证
  90. $result = $this->validate($data, 'User');
  91. // 验证失败 输出错误信息
  92. if(true !== $result) $this->error($result);
  93. // 非超级管理需要验证可选择角色
  94. if (session('user_auth.role') != 1) {
  95. if ($data['role'] == session('user_auth.role')) {
  96. $this->error('禁止创建与当前角色同级的用户');
  97. }
  98. $role_list = RoleModel::getChildsId(session('user_auth.role'));
  99. if (!in_array($data['role'], $role_list)) {
  100. $this->error('权限不足,禁止创建非法角色的用户');
  101. }
  102. if (isset($data['roles'])) {
  103. $deny_role = array_diff($data['roles'], $role_list);
  104. if ($deny_role) {
  105. $this->error('权限不足,附加角色设置错误');
  106. }
  107. }
  108. }
  109. $data['roles'] = isset($data['roles']) ? implode(',', $data['roles']) : '';
  110. if ($user = UserModel::create($data)) {
  111. Hook::listen('user_add', $user);
  112. // 记录行为
  113. action_log('user_add', 'admin_user', $user['id'], UID);
  114. $this->success('新增成功', url('index'));
  115. } else {
  116. $this->error('新增失败');
  117. }
  118. }
  119. // 角色列表
  120. if (session('user_auth.role') != 1) {
  121. $role_list = RoleModel::getTree(null, false, session('user_auth.role'));
  122. } else {
  123. $role_list = RoleModel::getTree(null, false);
  124. }
  125. // 使用ZBuilder快速创建表单
  126. return ZBuilder::make('form')
  127. ->setPageTitle('新增') // 设置页面标题
  128. ->addFormItems([ // 批量添加表单项
  129. ['text', 'username', '用户名', '必填,可由英文字母、数字组成'],
  130. ['text', 'nickname', '昵称', '可以是中文'],
  131. ['select', 'role', '主角色', '非超级管理员,禁止创建与当前角色同级的用户', $role_list],
  132. ['select', 'roles', '副角色', '可多选', $role_list, '', 'multiple'],
  133. ['text', 'email', '邮箱', ''],
  134. ['password', 'password', '密码', '必填,6-20位'],
  135. ['text', 'mobile', '手机号'],
  136. ['image', 'avatar', '头像'],
  137. ['radio', 'status', '状态', '', ['禁用', '启用'], 1]
  138. ])
  139. ->fetch();
  140. }
  141. /**
  142. * 编辑
  143. * @param null $id 用户id
  144. * @author 蔡伟明 <314013107@qq.com>
  145. * @return mixed
  146. * @throws \think\Exception
  147. * @throws \think\db\exception\DataNotFoundException
  148. * @throws \think\db\exception\ModelNotFoundException
  149. * @throws \think\exception\DbException
  150. */
  151. public function edit($id = null)
  152. {
  153. if ($id === null) $this->error('缺少参数');
  154. // 非超级管理员检查可编辑用户
  155. if (session('user_auth.role') != 1) {
  156. $role_list = RoleModel::getChildsId(session('user_auth.role'));
  157. $user_list = UserModel::where('role', 'in', $role_list)->column('id');
  158. if (!in_array($id, $user_list)) {
  159. $this->error('权限不足,没有可操作的用户');
  160. }
  161. }
  162. // 保存数据
  163. if ($this->request->isPost()) {
  164. $data = $this->request->post();
  165. // 禁止修改超级管理员的角色和状态
  166. if ($data['id'] == 1 && $data['role'] != 1) {
  167. $this->error('禁止修改超级管理员角色');
  168. }
  169. // 禁止修改超级管理员的状态
  170. if ($data['id'] == 1 && $data['status'] != 1) {
  171. $this->error('禁止修改超级管理员状态');
  172. }
  173. // 验证
  174. $result = $this->validate($data, 'User.update');
  175. // 验证失败 输出错误信息
  176. if(true !== $result) $this->error($result);
  177. // 如果没有填写密码,则不更新密码
  178. if ($data['password'] == '') {
  179. unset($data['password']);
  180. }
  181. // 非超级管理需要验证可选择角色
  182. if (session('user_auth.role') != 1) {
  183. if ($data['role'] == session('user_auth.role')) {
  184. $this->error('禁止修改为当前角色同级的用户');
  185. }
  186. $role_list = RoleModel::getChildsId(session('user_auth.role'));
  187. if (!in_array($data['role'], $role_list)) {
  188. $this->error('权限不足,禁止修改为非法角色的用户');
  189. }
  190. if (isset($data['roles'])) {
  191. $deny_role = array_diff($data['roles'], $role_list);
  192. if ($deny_role) {
  193. $this->error('权限不足,附加角色设置错误');
  194. }
  195. }
  196. }
  197. $data['roles'] = isset($data['roles']) ? implode(',', $data['roles']) : '';
  198. if (UserModel::update($data)) {
  199. $user = UserModel::get($data['id']);
  200. Hook::listen('user_edit', $user);
  201. // 记录行为
  202. action_log('user_edit', 'admin_user', $user['id'], UID, get_nickname($user['id']));
  203. $this->success('编辑成功', cookie('__forward__'));
  204. } else {
  205. $this->error('编辑失败');
  206. }
  207. }
  208. // 获取数据
  209. $info = UserModel::where('id', $id)->field('password', true)->find();
  210. // 角色列表
  211. if (session('user_auth.role') != 1) {
  212. $role_list = RoleModel::getTree(null, false, session('user_auth.role'));
  213. } else {
  214. $role_list = RoleModel::getTree(null, false);
  215. }
  216. // 使用ZBuilder快速创建表单
  217. return ZBuilder::make('form')
  218. ->setPageTitle('编辑') // 设置页面标题
  219. ->addFormItems([ // 批量添加表单项
  220. ['hidden', 'id'],
  221. ['static', 'username', '用户名', '不可更改'],
  222. ['text', 'nickname', '昵称', '可以是中文'],
  223. ['select', 'role', '主角色', '非超级管理员,禁止创建与当前角色同级的用户', $role_list],
  224. ['select', 'roles', '副角色', '可多选', $role_list, '', 'multiple'],
  225. ['text', 'email', '邮箱', ''],
  226. ['password', 'password', '密码', '必填,6-20位'],
  227. ['text', 'mobile', '手机号'],
  228. ['image', 'avatar', '头像'],
  229. ['radio', 'status', '状态', '', ['禁用', '启用']]
  230. ])
  231. ->setFormData($info) // 设置表单数据
  232. ->fetch();
  233. }
  234. /**
  235. * 授权
  236. * @param string $module 模块名
  237. * @param int $uid 用户id
  238. * @param string $tab 分组tab
  239. * @author 蔡伟明 <314013107@qq.com>
  240. * @return mixed
  241. * @throws \think\Exception
  242. * @throws \think\db\exception\DataNotFoundException
  243. * @throws \think\db\exception\ModelNotFoundException
  244. * @throws \think\exception\DbException
  245. * @throws \think\exception\PDOException
  246. */
  247. public function access($module = '', $uid = 0, $tab = '')
  248. {
  249. if ($uid === 0) $this->error('缺少参数');
  250. // 非超级管理员检查可编辑用户
  251. if (session('user_auth.role') != 1) {
  252. $role_list = RoleModel::getChildsId(session('user_auth.role'));
  253. $user_list = UserModel::where('role', 'in', $role_list)->column('id');
  254. if (!in_array($uid, $user_list)) {
  255. $this->error('权限不足,没有可操作的用户');
  256. }
  257. }
  258. // 获取所有授权配置信息
  259. $list_module = ModuleModel::where('access', 'neq', '')
  260. ->where('access', 'neq', '')
  261. ->where('status', 1)
  262. ->column('name,title,access');
  263. if ($list_module) {
  264. // tab分组信息
  265. $tab_list = [];
  266. foreach ($list_module as $key => $value) {
  267. $list_module[$key]['access'] = json_decode($value['access'], true);
  268. // 配置分组信息
  269. $tab_list[$value['name']] = [
  270. 'title' => $value['title'],
  271. 'url' => url('access', [
  272. 'module' => $value['name'],
  273. 'uid' => $uid
  274. ])
  275. ];
  276. }
  277. $module = $module == '' ? current(array_keys($list_module)) : $module;
  278. $this->assign('tab_nav', [
  279. 'tab_list' => $tab_list,
  280. 'curr_tab' => $module
  281. ]);
  282. // 读取授权内容
  283. $access = $list_module[$module]['access'];
  284. foreach ($access as $key => $value) {
  285. $access[$key]['url'] = url('access', [
  286. 'module' => $module,
  287. 'uid' => $uid,
  288. 'tab' => $key
  289. ]);
  290. }
  291. // 当前分组
  292. $tab = $tab == '' ? current(array_keys($access)) : $tab;
  293. // 当前授权
  294. $curr_access = $access[$tab];
  295. if (!isset($curr_access['nodes'])) {
  296. $this->error('模块:'.$module.' 数据授权配置缺少nodes信息');
  297. }
  298. $curr_access_nodes = $curr_access['nodes'];
  299. $this->assign('tab', $tab);
  300. $this->assign('access', $access);
  301. if ($this->request->isPost()) {
  302. $post = $this->request->param();
  303. if (isset($post['nodes'])) {
  304. $data_node = [];
  305. foreach ($post['nodes'] as $node) {
  306. list($group, $nid) = explode('|', $node);
  307. $data_node[] = [
  308. 'module' => $module,
  309. 'group' => $group,
  310. 'uid' => $uid,
  311. 'nid' => $nid,
  312. 'tag' => $post['tag']
  313. ];
  314. }
  315. // 先删除原有授权
  316. $map['module'] = $post['module'];
  317. $map['tag'] = $post['tag'];
  318. $map['uid'] = $post['uid'];
  319. if (false === AccessModel::where($map)->delete()) {
  320. $this->error('清除旧授权失败');
  321. }
  322. // 添加新的授权
  323. $AccessModel = new AccessModel;
  324. if (!$AccessModel->saveAll($data_node)) {
  325. $this->error('操作失败');
  326. }
  327. // 调用后置方法
  328. if (isset($curr_access_nodes['model_name']) && $curr_access_nodes['model_name'] != '') {
  329. if (strpos($curr_access_nodes['model_name'], '/')) {
  330. list($module, $model_name) = explode('/', $curr_access_nodes['model_name']);
  331. } else {
  332. $model_name = $curr_access_nodes['model_name'];
  333. }
  334. $class = "app\\{$module}\\model\\".$model_name;
  335. $model = new $class;
  336. try{
  337. $model->afterAccessUpdate($post);
  338. }catch(\Exception $e){}
  339. }
  340. // 记录行为
  341. $nids = implode(',', $post['nodes']);
  342. $details = "模块($module),分组(".$post['tag']."),授权节点ID($nids)";
  343. action_log('user_access', 'admin_user', $uid, UID, $details);
  344. $this->success('操作成功', url('access', ['uid' => $post['uid'], 'module' => $module, 'tab' => $tab]));
  345. } else {
  346. // 清除所有数据授权
  347. $map['module'] = $post['module'];
  348. $map['tag'] = $post['tag'];
  349. $map['uid'] = $post['uid'];
  350. if (false === AccessModel::where($map)->delete()) {
  351. $this->error('清除旧授权失败');
  352. } else {
  353. $this->success('操作成功');
  354. }
  355. }
  356. } else {
  357. $nodes = [];
  358. if (isset($curr_access_nodes['model_name']) && $curr_access_nodes['model_name'] != '') {
  359. if (strpos($curr_access_nodes['model_name'], '/')) {
  360. list($module, $model_name) = explode('/', $curr_access_nodes['model_name']);
  361. } else {
  362. $model_name = $curr_access_nodes['model_name'];
  363. }
  364. $class = "app\\{$module}\\model\\".$model_name;
  365. $model = new $class;
  366. try{
  367. $nodes = $model->access();
  368. }catch(\Exception $e){
  369. $this->error('模型:'.$class."缺少“access”方法");
  370. }
  371. } else {
  372. // 没有设置模型名,则按表名获取数据
  373. $fields = [
  374. $curr_access_nodes['primary_key'],
  375. $curr_access_nodes['parent_id'],
  376. $curr_access_nodes['node_name']
  377. ];
  378. $nodes = Db::name($curr_access_nodes['table_name'])->order($curr_access_nodes['primary_key'])->field($fields)->select();
  379. $tree_config = [
  380. 'title' => $curr_access_nodes['node_name'],
  381. 'id' => $curr_access_nodes['primary_key'],
  382. 'pid' => $curr_access_nodes['parent_id']
  383. ];
  384. $nodes = Tree::config($tree_config)->toLayer($nodes);
  385. }
  386. // 查询当前用户的权限
  387. $map = [
  388. 'module' => $module,
  389. 'tag' => $tab,
  390. 'uid' => $uid
  391. ];
  392. $node_access = AccessModel::where($map)->select();
  393. $user_access = [];
  394. foreach ($node_access as $item) {
  395. $user_access[$item['group'].'|'.$item['nid']] = 1;
  396. }
  397. $nodes = $this->buildJsTree($nodes, $curr_access_nodes, $user_access);
  398. $this->assign('nodes', $nodes);
  399. }
  400. $page_tips = isset($curr_access['page_tips']) ? $curr_access['page_tips'] : '';
  401. $tips_type = isset($curr_access['tips_type']) ? $curr_access['tips_type'] : 'info';
  402. $this->assign('page_tips', $page_tips);
  403. $this->assign('tips_type', $tips_type);
  404. }
  405. $this->assign('module', $module);
  406. $this->assign('uid', $uid);
  407. $this->assign('tab', $tab);
  408. $this->assign('page_title', '数据授权');
  409. return $this->fetch();
  410. }
  411. /**
  412. * 构建jstree代码
  413. * @param array $nodes 节点
  414. * @param array $curr_access 当前授权信息
  415. * @param array $user_access 用户授权信息
  416. * @author 蔡伟明 <314013107@qq.com>
  417. * @return string
  418. */
  419. private function buildJsTree($nodes = [], $curr_access = [], $user_access = [])
  420. {
  421. $result = '';
  422. if (!empty($nodes)) {
  423. $option = [
  424. 'opened' => true,
  425. 'selected' => false
  426. ];
  427. foreach ($nodes as $node) {
  428. $key = $curr_access['group'].'|'.$node[$curr_access['primary_key']];
  429. $option['selected'] = isset($user_access[$key]) ? true : false;
  430. if (isset($node['child'])) {
  431. $curr_access_child = isset($curr_access['child']) ? $curr_access['child'] : $curr_access;
  432. $result .= '<li id="'.$key.'" data-jstree=\''.json_encode($option).'\'>'.$node[$curr_access['node_name']].$this->buildJsTree($node['child'], $curr_access_child, $user_access).'</li>';
  433. } else {
  434. $result .= '<li id="'.$key.'" data-jstree=\''.json_encode($option).'\'>'.$node[$curr_access['node_name']].'</li>';
  435. }
  436. }
  437. }
  438. return '<ul>'.$result.'</ul>';
  439. }
  440. /**
  441. * 删除用户
  442. * @param array $ids 用户id
  443. * @author 蔡伟明 <314013107@qq.com>
  444. * @throws \think\Exception
  445. * @throws \think\exception\PDOException
  446. */
  447. public function delete($ids = [])
  448. {
  449. Hook::listen('user_delete', $ids);
  450. return $this->setStatus('delete');
  451. }
  452. /**
  453. * 启用用户
  454. * @param array $ids 用户id
  455. * @author 蔡伟明 <314013107@qq.com>
  456. * @throws \think\Exception
  457. * @throws \think\exception\PDOException
  458. */
  459. public function enable($ids = [])
  460. {
  461. Hook::listen('user_enable', $ids);
  462. return $this->setStatus('enable');
  463. }
  464. /**
  465. * 禁用用户
  466. * @param array $ids 用户id
  467. * @author 蔡伟明 <314013107@qq.com>
  468. * @throws \think\Exception
  469. * @throws \think\exception\PDOException
  470. */
  471. public function disable($ids = [])
  472. {
  473. Hook::listen('user_disable', $ids);
  474. return $this->setStatus('disable');
  475. }
  476. /**
  477. * 设置用户状态:删除、禁用、启用
  478. * @param string $type 类型:delete/enable/disable
  479. * @param array $record
  480. * @author 蔡伟明 <314013107@qq.com>
  481. * @throws \think\Exception
  482. * @throws \think\exception\PDOException
  483. */
  484. public function setStatus($type = '', $record = [])
  485. {
  486. $ids = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
  487. $ids = (array)$ids;
  488. // 当前用户所能操作的用户
  489. $role_list = RoleModel::getChildsId(session('user_auth.role'));
  490. $user_list = UserModel::where('role', 'in', $role_list)->column('id');
  491. if (session('user_auth.role') != 1 && !$user_list) {
  492. $this->error('权限不足,没有可操作的用户');
  493. }
  494. $ids = array_intersect($user_list, $ids);
  495. if (!$ids) {
  496. $this->error('权限不足,没有可操作的用户');
  497. }
  498. switch ($type) {
  499. case 'enable':
  500. if (false === UserModel::where('id', 'in', $ids)->setField('status', 1)) {
  501. $this->error('启用失败');
  502. }
  503. break;
  504. case 'disable':
  505. if (false === UserModel::where('id', 'in', $ids)->setField('status', 0)) {
  506. $this->error('禁用失败');
  507. }
  508. break;
  509. case 'delete':
  510. if (false === UserModel::where('id', 'in', $ids)->delete()) {
  511. $this->error('删除失败');
  512. }
  513. break;
  514. default:
  515. $this->error('非法操作');
  516. }
  517. action_log('user_'.$type, 'admin_user', '', UID);
  518. $this->success('操作成功');
  519. }
  520. /**
  521. * 快速编辑
  522. * @param array $record 行为日志
  523. * @author 蔡伟明 <314013107@qq.com>
  524. * @return mixed
  525. */
  526. public function quickEdit($record = [])
  527. {
  528. $id = input('post.pk', '');
  529. $id == UID && $this->error('禁止操作当前账号');
  530. $field = input('post.name', '');
  531. $value = input('post.value', '');
  532. // 非超级管理员检查可操作的用户
  533. if (session('user_auth.role') != 1) {
  534. $role_list = RoleModel::getChildsId(session('user_auth.role'));
  535. $user_list = UserModel::where('role', 'in', $role_list)->column('id');
  536. if (!in_array($id, $user_list)) {
  537. $this->error('权限不足,没有可操作的用户');
  538. }
  539. }
  540. $config = UserModel::where('id', $id)->value($field);
  541. $details = '字段(' . $field . '),原值(' . $config . '),新值:(' . $value . ')';
  542. return parent::quickEdit(['user_edit', 'admin_user', $id, UID, $details]);
  543. }
  544. }