Category.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. <?php
  2. namespace app\admin\controller\cms;
  3. use app\admin\model\cms\Category as CategoryModel;
  4. use app\admin\model\cms\CategoryData;
  5. use app\admin\model\cms\CategoryPriv as CategoryPrivModel;
  6. use app\admin\model\cms\Models;
  7. use app\admin\model\general\Lang as LangModel;
  8. use app\admin\model\user\UserGroup;
  9. use app\common\controller\Backend;
  10. use think\Exception;
  11. use think\exception\ValidateException;
  12. use think\facade\Cache;
  13. use think\facade\Db;
  14. class Category extends Backend
  15. {
  16. protected $noNeedRight = ['count_items', 'public_cache', 'public_tpl_file_list'];
  17. protected function initialize()
  18. {
  19. parent::initialize();
  20. $this->modelClass = new CategoryModel;
  21. }
  22. public function index()
  23. {
  24. if ($this->request->isAjax()) {
  25. $models = Models::where('status', 1)->column('name', 'id');
  26. $tree = new \util\Tree();
  27. $tree->icon = ['&nbsp;&nbsp;&nbsp;│ ', '&nbsp;&nbsp;&nbsp;├─ ', '&nbsp;&nbsp;&nbsp;└─ '];
  28. $tree->nbsp = '&nbsp;&nbsp;&nbsp;';
  29. $categorys = [];
  30. $result = CategoryModel::order('listorder DESC, id DESC')->select();
  31. $enabledLangs = LangModel::getEnabledList();
  32. foreach ($result as $v) {
  33. $translate = CategoryModel::getTranslate($v['id'], LangModel::getDefaultMark());
  34. if ($translate) {
  35. $v['catname'] = $translate['catname'];
  36. }
  37. $statusMap = [];
  38. foreach ($enabledLangs as $langItem) {
  39. $statusMap[$langItem->mark] = CategoryData::where(['catid' => $v['id'], 'lang' => $langItem->mark])->count() > 0 ? 1 : 0;
  40. }
  41. $v['lang_status'] = $statusMap;
  42. $v['modelname'] = $models[$v['modelid']] ?? '/';
  43. $v['url'] = buildCatUrl($v['id'], $v['url']);
  44. $categorys[$v['id']] = $v;
  45. }
  46. $tree->init($categorys);
  47. $_list = $tree->getTreeList($tree->getTreeArray(0), 'catname');
  48. return json(['code' => 0, 'count' => count($_list), 'data' => $_list]);
  49. }
  50. $langOptions = [];
  51. foreach (LangModel::getEnabledList() as $item) {
  52. $langOptions[] = [
  53. 'mark' => $item->mark,
  54. 'name' => $item->name,
  55. 'code' => $item->code,
  56. ];
  57. }
  58. $this->assignconfig('enabledLangs', $langOptions);
  59. return $this->fetch();
  60. }
  61. public function add()
  62. {
  63. if ($this->request->isPost()) {
  64. $data = $this->request->post('row/a');
  65. $data['setting'] = $data['setting'] ?? [];
  66. $lang = $data['lang'] ?? LangModel::getDefaultMark();
  67. switch ($data['type']) {
  68. case 1: $scene = 'page'; break;
  69. case 2: $scene = 'list'; break;
  70. case 3: $scene = 'link'; break;
  71. default: $this->error('栏目类型错误~');
  72. }
  73. if (!empty($data['isbatch'])) {
  74. $this->error('当前多语言版暂不支持栏目批量添加,请使用单条添加');
  75. }
  76. $data['catdir'] = $this->get_dirpinyin($data['catname'] ?? '', $data['catdir'] ?? '');
  77. Db::startTrans();
  78. try {
  79. $this->validate($data, 'app\\admin\\validate\\cms\\Category.' . $scene);
  80. $this->modelClass->save($data);
  81. CategoryModel::saveTranslation($this->modelClass->id, [
  82. 'catname' => $data['catname'] ?? '',
  83. 'description' => $data['description'] ?? '',
  84. 'meta_title' => $data['setting']['meta_title'] ?? '',
  85. 'meta_keywords' => $data['setting']['meta_keywords'] ?? '',
  86. 'meta_description' => $data['setting']['meta_description'] ?? '',
  87. ], $lang);
  88. $this->refreshCategoryRelationCache();
  89. Db::commit();
  90. } catch (ValidateException $e) {
  91. Db::rollback();
  92. $this->error($e->getMessage());
  93. } catch (\Throwable $e) {
  94. Db::rollback();
  95. $this->error($e->getMessage());
  96. }
  97. $this->success('添加成功!');
  98. }
  99. $parentid = $this->request->param('parentid/d', 0);
  100. $Ca = [];
  101. if (!empty($parentid)) {
  102. $Ca = getCategory($parentid);
  103. if (empty($Ca)) {
  104. $this->error('父栏目不存在!');
  105. }
  106. }
  107. $models = Models::where('status', 1)->where('module', 'cms')->select();
  108. $array = CategoryModel::order('listorder DESC, id DESC')->column('*', 'id');
  109. if (!empty($array) && is_array($array)) {
  110. $tree = new \util\Tree();
  111. $tree->icon = ['&nbsp;&nbsp;│ ', '&nbsp;&nbsp;├─ ', '&nbsp;&nbsp;└─ '];
  112. $tree->nbsp = '&nbsp;&nbsp;';
  113. $str = '<option value=@id @selected @disabled>@spacer @catname</option>';
  114. $tree->init($array);
  115. $categorydata = $tree->getTree(0, $str, $parentid);
  116. } else {
  117. $categorydata = '';
  118. }
  119. $this->assign([
  120. 'category' => $categorydata,
  121. 'models' => $models,
  122. 'parentid_modelid' => $Ca['modelid'] ?? 0,
  123. 'userGroup' => UserGroup::column('name', 'id'),
  124. 'cmsConfig' => get_addon_config('cms'),
  125. 'langs' => LangModel::getEnabledList(),
  126. 'currentLang' => LangModel::getDefaultMark(),
  127. ]);
  128. $this->assignconfig('parentid_modelid', $Ca['modelid'] ?? 0);
  129. return $this->fetch();
  130. }
  131. public function edit()
  132. {
  133. if ($this->request->isPost()) {
  134. $catid = $this->request->param('id/d', 0);
  135. $row = Db::name('cms_category')->where('id', $catid)->find();
  136. if (!$row) {
  137. $this->error('记录未找到');
  138. }
  139. $row['setting'] = empty($row['setting']) ? [] : json_decode($row['setting'], true);
  140. $data = $this->request->post('row/a');
  141. $data['setting'] = $data['setting'] ?? [];
  142. $lang = $data['lang'] ?? LangModel::getDefaultMark();
  143. if (($data['parentid'] ?? 0) == $catid) {
  144. $this->error('上级栏目不能是自身!');
  145. }
  146. switch ($data['type']) {
  147. case 1: $data['modelid'] = 0; $scene = 'page'; break;
  148. case 2: $scene = 'list'; break;
  149. case 3: $data['modelid'] = 0; $scene = 'link'; break;
  150. default: $this->error('栏目类型错误~');
  151. }
  152. if ($lang === LangModel::getDefaultMark()) {
  153. $data['catdir'] = $this->get_dirpinyin($data['catname'] ?? '', $data['catdir'] ?? '', $catid);
  154. } else {
  155. $data['catdir'] = $row['catdir'];
  156. }
  157. Db::startTrans();
  158. try {
  159. $mainData = $data;
  160. if ($lang !== LangModel::getDefaultMark()) {
  161. $mainData['catname'] = $row['catname'];
  162. $mainData['catdir'] = $row['catdir'];
  163. $mainData['description'] = $row['description'];
  164. $mainData['setting'] = [
  165. 'meta_title' => $row['setting']['meta_title'] ?? '',
  166. 'meta_keywords' => $row['setting']['meta_keywords'] ?? '',
  167. 'meta_description' => $row['setting']['meta_description'] ?? '',
  168. 'category_template' => $row['setting']['category_template'] ?? '',
  169. 'list_template' => $row['setting']['list_template'] ?? '',
  170. 'show_template' => $row['setting']['show_template'] ?? '',
  171. 'page_template' => $row['setting']['page_template'] ?? '',
  172. ];
  173. }
  174. $this->validate($mainData, 'app\\admin\\validate\\cms\\Category.' . $scene);
  175. Db::name('cms_category')->where('id', $catid)->update([
  176. 'catname' => $mainData['catname'] ?? '',
  177. 'catdir' => $mainData['catdir'] ?? '',
  178. 'type' => $mainData['type'] ?? 1,
  179. 'target' => $mainData['target'] ?? '_self',
  180. 'modelid' => $mainData['modelid'] ?? 0,
  181. 'parentid' => $mainData['parentid'] ?? 0,
  182. 'image' => $mainData['image'] ?? '',
  183. 'icon' => $mainData['icon'] ?? '',
  184. 'description' => $mainData['description'] ?? '',
  185. 'url' => $mainData['url'] ?? '',
  186. 'setting' => json_encode($mainData['setting'] ?? [], JSON_UNESCAPED_UNICODE),
  187. 'listorder' => $mainData['listorder'] ?? 0,
  188. 'status' => $mainData['status'] ?? 0,
  189. ]);
  190. CategoryModel::saveTranslation($catid, [
  191. 'catname' => $data['catname'] ?? '',
  192. 'description' => $data['description'] ?? '',
  193. 'meta_title' => $data['setting']['meta_title'] ?? '',
  194. 'meta_keywords' => $data['setting']['meta_keywords'] ?? '',
  195. 'meta_description' => $data['setting']['meta_description'] ?? '',
  196. ], $lang);
  197. Cache::delete('getCategory_' . $catid);
  198. $this->refreshCategoryRelationCache();
  199. Db::commit();
  200. } catch (ValidateException $e) {
  201. Db::rollback();
  202. $this->error($e->getMessage());
  203. } catch (\Throwable $e) {
  204. Db::rollback();
  205. $this->error($e->getMessage());
  206. }
  207. $this->success('修改成功!');
  208. }
  209. $catid = $this->request->param('id/d', 0);
  210. $lang = $this->request->param('lang/s', LangModel::getDefaultMark());
  211. if (empty($catid)) {
  212. $this->error('请选择需要修改的栏目!');
  213. }
  214. $data = CategoryModel::find($catid);
  215. $translate = CategoryModel::getTranslate($catid, $lang);
  216. $setting = $data['setting'];
  217. if ($translate) {
  218. $data['catname'] = $translate['catname'];
  219. $data['description'] = $translate['description'];
  220. $setting['meta_title'] = $translate['meta_title'];
  221. $setting['meta_keywords'] = $translate['meta_keywords'];
  222. $setting['meta_description'] = $translate['meta_description'];
  223. }
  224. $models = Models::where('status', 1)->where('module', 'cms')->select();
  225. $array = CategoryModel::order('listorder DESC, id DESC')->column('*', 'id');
  226. if (!empty($array) && is_array($array)) {
  227. $tree = new \util\Tree();
  228. $tree->icon = ['&nbsp;&nbsp;│ ', '&nbsp;&nbsp;├─ ', '&nbsp;&nbsp;└─ '];
  229. $tree->nbsp = '&nbsp;&nbsp;';
  230. $str = '<option value=@id @selected @disabled>@spacer @catname</option>';
  231. $tree->init($array);
  232. $categorydata = $tree->getTree(0, $str, $data['parentid']);
  233. } else {
  234. $categorydata = '';
  235. }
  236. $langStatus = [];
  237. foreach (LangModel::getEnabledList() as $item) {
  238. $langStatus[$item->mark] = CategoryData::where(['catid' => $catid, 'lang' => $item->mark])->count() > 0 ? 1 : 0;
  239. }
  240. $this->assign([
  241. 'data' => $data,
  242. 'setting' => $setting,
  243. 'category' => $categorydata,
  244. 'models' => $models,
  245. 'privs' => CategoryPrivModel::where('catid', $catid)->select(),
  246. 'cmsConfig' => get_addon_config('cms'),
  247. 'userGroup' => UserGroup::column('name', 'id'),
  248. 'langs' => LangModel::getEnabledList(),
  249. 'currentLang' => $lang,
  250. 'defaultLang' => LangModel::getDefaultMark(),
  251. 'langStatus' => $langStatus,
  252. ]);
  253. return $this->fetch();
  254. }
  255. public function cat_priv()
  256. {
  257. $act = $this->request->param('act');
  258. $id = $this->request->param('id');
  259. if ($act == 'authorization') {
  260. if (empty($id)) {
  261. $this->error('请指定需要授权的角色!');
  262. }
  263. if ($this->request->isAjax()) {
  264. $data = $this->request->post();
  265. $priv = [];
  266. if (isset($data['priv'])) {
  267. foreach ($data['priv'] as $k => $v) {
  268. foreach ($v as $q) {
  269. $priv[] = ['roleid' => $id, 'catid' => $k, 'action' => $q, 'is_admin' => 1];
  270. }
  271. }
  272. CategoryPrivModel::where('roleid', $id)->delete();
  273. (new CategoryPrivModel)->saveAll($priv);
  274. $this->success('栏目授权成功!');
  275. }
  276. $this->error('请指定需要授权的栏目!');
  277. }
  278. $tree = new \util\Tree();
  279. $tree->icon = ['&nbsp;&nbsp;&nbsp;│ ', '&nbsp;&nbsp;&nbsp;├─ ', '&nbsp;&nbsp;&nbsp;└─ '];
  280. $tree->nbsp = '&nbsp;&nbsp;&nbsp;';
  281. $category_priv = CategoryPrivModel::where('roleid', $id)->select()->toArray();
  282. $priv = [];
  283. foreach ($category_priv as $v) {
  284. $priv[$v['catid']][$v['action']] = true;
  285. }
  286. $categorys = CategoryModel::order('listorder DESC, id DESC')->select()->toArray();
  287. foreach ($categorys as $k => $v) {
  288. if ($v['type'] == 1 || $v['child']) {
  289. $v['disabled'] = 'disabled';
  290. $v['init_check'] = $v['add_check'] = $v['delete_check'] = $v['listorder_check'] = $v['move_check'] = $v['edit_check'] = $v['status_check'] = '';
  291. } else {
  292. $v['disabled'] = '';
  293. $v['add_check'] = isset($priv[$v['id']]['add']) ? 'checked' : '';
  294. $v['delete_check'] = isset($priv[$v['id']]['delete']) ? 'checked' : '';
  295. $v['listorder_check'] = isset($priv[$v['id']]['listorder']) ? 'checked' : '';
  296. $v['move_check'] = isset($priv[$v['id']]['remove']) ? 'checked' : '';
  297. $v['edit_check'] = isset($priv[$v['id']]['edit']) ? 'checked' : '';
  298. $v['status_check'] = isset($priv[$v['id']]['status']) ? 'checked' : '';
  299. }
  300. $v['init_check'] = isset($priv[$v['id']]['init']) ? 'checked' : '';
  301. $categorys[$k] = $v;
  302. }
  303. $str = "<tr>
  304. <td align='center'><input type='checkbox' value='1' data-name='@id' lay-skin='primary'></td>
  305. <td>@spacer@catname</td>
  306. <td align='center'><input type='checkbox' name='priv[@id][]' @init_check lay-skin='primary' value='init'></td>
  307. <td align='center'><input type='checkbox' name='priv[@id][]' @disabled @add_check lay-skin='primary' value='add'></td>
  308. <td align='center'><input type='checkbox' name='priv[@id][]' @disabled @edit_check lay-skin='primary' value='edit'></td>
  309. <td align='center'><input type='checkbox' name='priv[@id][]' @disabled @delete_check lay-skin='primary' value='delete'></td>
  310. <td align='center'><input type='checkbox' name='priv[@id][]' @disabled @listorder_check lay-skin='primary' value='listorder'></td>
  311. <td align='center'><input type='checkbox' name='priv[@id][]' @disabled @status_check lay-skin='primary' value='status'></td>
  312. <td align='center'><input type='checkbox' name='priv[@id][]' @disabled @move_check lay-skin='primary' value='remove'></td>
  313. </tr>";
  314. $tree->init($categorys);
  315. $this->assign('categorys', $tree->getTree(0, $str));
  316. return $this->fetch('authorization');
  317. } elseif ($act == 'remove') {
  318. CategoryPrivModel::where('roleid', $id)->delete();
  319. $this->success('删除成功!');
  320. }
  321. if ($this->request->isAjax()) {
  322. $priv_num = [];
  323. $category_priv = CategoryPrivModel::field('count(*) as num,roleid')->group('roleid')->select()->toArray();
  324. foreach ($category_priv as $v) {
  325. $priv_num[$v['roleid']] = $v['num'];
  326. }
  327. $list = Db::name('AuthGroup')->where('status', 1)->order('id', 'desc')->field('id,title')->select()->toArray();
  328. foreach ($list as $k => $v) {
  329. $list[$k]['admin'] = $v['id'] == 1;
  330. $list[$k]['num'] = $priv_num[$v['id']] ?? 0;
  331. }
  332. return json(['code' => 0, 'data' => $list]);
  333. }
  334. $this->assign('cmsConfig', get_addon_config('cms'));
  335. return $this->fetch();
  336. }
  337. public function public_cache()
  338. {
  339. try {
  340. $this->refreshCategoryRelationCache();
  341. } catch (Exception $e) {
  342. $this->error($e->getMessage());
  343. }
  344. $this->success('更新缓存成功!');
  345. }
  346. private function refreshCategoryRelationCache()
  347. {
  348. $categorys = CategoryModel::order('listorder DESC, id DESC')->column('*', 'id');
  349. if (empty($categorys)) {
  350. return true;
  351. }
  352. foreach ($categorys as $catid => $cat) {
  353. $arrparentid = CategoryModel::get_arrparentid($catid);
  354. $arrchildid = CategoryModel::get_arrchildid($catid, true);
  355. $child = is_numeric($arrchildid) ? 0 : 1;
  356. if ($cat['arrparentid'] != $arrparentid || $cat['arrchildid'] != $arrchildid || $cat['child'] != $child) {
  357. CategoryModel::where('id', $catid)->update(['arrparentid' => $arrparentid, 'arrchildid' => $arrchildid, 'child' => $child]);
  358. }
  359. Cache::delete('getCategory_' . $catid);
  360. if ($cat['parentid'] != 0 && !isset($categorys[$cat['parentid']])) {
  361. CategoryModel::destroy($catid);
  362. }
  363. }
  364. cms_cache('Category', null);
  365. return true;
  366. }
  367. public function count_items()
  368. {
  369. $result = CategoryModel::order('listorder DESC, id DESC')->select();
  370. $models = Models::column('tablename', 'id');
  371. foreach ($result as $r) {
  372. if ($r['type'] == 2 && isset($models[$r['modelid']])) {
  373. $number = Db::name(ucwords($models[$r['modelid']]))->where('catid', $r['id'])->count();
  374. CategoryModel::where('id', $r['id'])->update(['items' => $number]);
  375. } else {
  376. CategoryModel::where('id', $r['id'])->update(['items' => 0]);
  377. }
  378. }
  379. $this->success('栏目数量校正成功!');
  380. }
  381. public function multi()
  382. {
  383. $id = $this->request->param('id/d');
  384. cms_cache('Category', null);
  385. getCategory($id, '', true);
  386. return parent::multi();
  387. }
  388. private function get_dirpinyin($catname = '', $catdir = '', $id = 0)
  389. {
  390. $pinyin = new \Overtrue\Pinyin\Pinyin('Overtrue\Pinyin\MemoryFileDictLoader');
  391. if (empty($catdir)) {
  392. $catdir = $pinyin->permalink($catname, '');
  393. }
  394. if (strval(intval($catdir)) == strval($catdir)) {
  395. $catdir .= genRandomString(3);
  396. }
  397. $map = [['catdir', '=', $catdir]];
  398. if ((int)$id > 0) {
  399. $map[] = ['id', '<>', $id];
  400. }
  401. $result = Db::name('cms_category')->field('id')->where($map)->find();
  402. if (!empty($result)) {
  403. return $this->get_dirpinyin($catname, $catdir . genRandomString(3), $id);
  404. }
  405. return $catdir;
  406. }
  407. public function public_tpl_file_list()
  408. {
  409. $id = $this->request->param('id/d');
  410. $data = Models::where('id', $id)->find();
  411. if ($data) {
  412. $this->success('', null, ['data' => $data['setting']]);
  413. }
  414. }
  415. }