Crud.php 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  1. <?php
  2. namespace app\admin\controller\crud;
  3. use think\Exception;
  4. use think\facade\Db;
  5. use app\admin\model\CrudLog;
  6. use app\common\library\Menu;
  7. use app\common\controller\Backend;
  8. use app\admin\library\crud\Helper;
  9. use think\db\exception\PDOException;
  10. class Crud extends Backend
  11. {
  12. /**
  13. * 模型文件数据
  14. */
  15. protected $modelData = [];
  16. /**
  17. * 控制器文件数据
  18. */
  19. protected $controllerData = [];
  20. /**
  21. * index.vue文件数据
  22. */
  23. protected $indexVueData = [];
  24. /**
  25. * form.vue文件数据
  26. */
  27. protected $formVueData = [];
  28. /**
  29. * 语言翻译前缀
  30. */
  31. protected $webTranslate = '';
  32. /**
  33. * 语言包数据
  34. */
  35. protected $langTsData = [];
  36. /**
  37. * 当designType为以下值时:
  38. * 1. 出入库字符串到数组转换
  39. * 2. 默认值转数组
  40. */
  41. protected $dtStringToArray = ['checkbox', 'selects', 'remoteSelects', 'city', 'images', 'files'];
  42. protected $noNeedPermission = ['logStart', 'getFileData', 'parseFieldData', 'generateCheck', 'databaseList'];
  43. public function initialize()
  44. {
  45. parent::initialize();
  46. $this->request->filter(['trim']);
  47. }
  48. public function generate()
  49. {
  50. $table = $this->request->post('table', []);
  51. $fields = $this->request->post('fields', []);
  52. if (!$table || !$fields || !isset($table['name']) || !$table['name']) {
  53. $this->error(__('Parameter error'));
  54. }
  55. try {
  56. // 记录日志
  57. $crudLogId = Helper::recordCrudStatus([
  58. 'table' => $table,
  59. 'fields' => $fields,
  60. 'status' => 'start',
  61. ]);
  62. // 表存在则删除
  63. Helper::delTable($table['name']);
  64. // 创建表
  65. [$tablePk] = Helper::createTable($table['name'], $table['comment'] ?? '', $fields);
  66. // 表名称
  67. $tableName = Helper::getTableName($table['name'], false);
  68. // 表注释
  69. $tableComment = mb_substr($table['comment'], -1) == '表' ? mb_substr($table['comment'], 0, -1) . '管理' : $table['comment'];
  70. // 生成文件信息解析
  71. $modelFile = Helper::parseNameData('admin', $tableName, '', 'model', $table['modelFile']);
  72. $validateFile = Helper::parseNameData('admin', $tableName, '', 'validate', $table['validateFile']);
  73. $controllerFile = Helper::parseNameData('admin', $tableName, '', 'controller', $table['controllerFile']);
  74. $webViewsDir = Helper::parseWebDirNameData($tableName, '', 'views', $table['webViewsDir']);
  75. $webLangDir = Helper::parseWebDirNameData($tableName, '', 'lang', $table['webViewsDir']);
  76. // 语言翻译前缀
  77. $this->webTranslate = implode('.', $webLangDir['lang']) . '.';
  78. // 快速搜索字段
  79. if (!in_array($tablePk, $table['quickSearchField'])) {
  80. $table['quickSearchField'][] = $tablePk;
  81. }
  82. $quickSearchFieldZhCnTitle = [];
  83. // 模型数据
  84. $this->modelData['append'] = [];
  85. $this->modelData['methods'] = [];
  86. $this->modelData['fieldType'] = [];
  87. $this->modelData['createTime'] = '';
  88. $this->modelData['updateTime'] = '';
  89. $this->modelData['beforeInsertMixins'] = [];
  90. $this->modelData['beforeInsert'] = '';
  91. $this->modelData['afterInsert'] = '';
  92. $this->modelData['name'] = $tableName;
  93. $this->modelData['className'] = $modelFile['lastName'];
  94. $this->modelData['namespace'] = $modelFile['namespace'];
  95. $this->modelData['relationMethodList'] = [];
  96. // 控制器数据
  97. $this->controllerData['attr'] = [];
  98. $this->controllerData['methods'] = [];
  99. $this->controllerData['filterRule'] = '';
  100. $this->controllerData['className'] = $controllerFile['lastName'];
  101. $this->controllerData['namespace'] = $controllerFile['namespace'];
  102. $this->controllerData['tableComment'] = $tableComment;
  103. $this->controllerData['modelName'] = $modelFile['lastName'];
  104. $this->controllerData['modelNamespace'] = $modelFile['namespace'];
  105. // index.vue数据
  106. $this->indexVueData['enableDragSort'] = false;
  107. $this->indexVueData['defaultItems'] = [];
  108. $this->indexVueData['tableColumn'] = [
  109. [
  110. 'type' => 'selection',
  111. 'align' => 'center',
  112. 'operator' => 'false',
  113. ],
  114. ];
  115. $this->indexVueData['dblClickNotEditColumn'] = ['undefined'];
  116. $this->indexVueData['optButtons'] = ['edit', 'delete'];
  117. $this->indexVueData['defaultOrder'] = '';
  118. // form.vue数据
  119. $this->formVueData['bigDialog'] = 'false';
  120. $this->formVueData['formFields'] = [];
  121. // 语言包数据
  122. $this->langTsData = [
  123. 'en' => [],
  124. 'zh-cn' => [],
  125. ];
  126. // 简化的字段数据
  127. $fieldsMap = [];
  128. foreach ($fields as $key => $field) {
  129. $fieldsMap[$field['name']] = $field['designType'];
  130. // 分析字段
  131. Helper::analyseField($field);
  132. Helper::getDictData($this->langTsData['en'], $field, 'en');
  133. Helper::getDictData($this->langTsData['zh-cn'], $field, 'zh-cn');
  134. // 快速搜索字段
  135. if (in_array($field['name'], $table['quickSearchField'])) {
  136. $quickSearchFieldZhCnTitle[] = $this->langTsData['zh-cn'][$field['name']] ?? $field['name'];
  137. }
  138. // 不允许双击编辑的字段
  139. if ($field['designType'] == 'switch') {
  140. $this->indexVueData['dblClickNotEditColumn'][] = $field['name'];
  141. }
  142. // 列字典数据
  143. $columnDict = $this->getColumnDict($field);
  144. // 表单项
  145. if (in_array($field['name'], $table['formFields'])) {
  146. $this->formVueData['formFields'][] = $this->getFormField($field, $columnDict);
  147. }
  148. // 表格列
  149. if (in_array($field['name'], $table['columnFields'])) {
  150. $this->indexVueData['tableColumn'][] = $this->getTableColumn($field, $columnDict);
  151. }
  152. // 关联表数据解析
  153. if (in_array($field['designType'], ['remoteSelect', 'remoteSelects'])) {
  154. $this->parseJoinData($field);
  155. }
  156. // 模型方法
  157. $this->parseModelMethods($field, $this->modelData);
  158. // 控制器/模型等文件的一些杂项属性解析
  159. $this->parseSundryData($field, $table);
  160. if (!in_array($field['name'], $table['formFields'])) {
  161. $this->controllerData['attr']['preExcludeFields'][] = $field['name'];
  162. }
  163. }
  164. // 快速搜索提示
  165. $this->langTsData['en']['quick Search Fields'] = implode(',', $table['quickSearchField']);
  166. $this->langTsData['zh-cn']['quick Search Fields'] = implode('、', $quickSearchFieldZhCnTitle);
  167. $this->controllerData['attr']['quickSearchField'] = $table['quickSearchField'];
  168. // 开启字段排序
  169. $weighKey = array_search('weigh', $fieldsMap);
  170. if ($weighKey !== false) {
  171. $this->indexVueData['enableDragSort'] = true;
  172. $this->modelData['afterInsert'] = Helper::assembleStub('mixins/model/afterInsert', [
  173. 'field' => $weighKey
  174. ]);
  175. }
  176. // 表格的操作列
  177. $this->indexVueData['tableColumn'][] = [
  178. 'label' => "t('operate')",
  179. 'align' => 'center',
  180. 'width' => $this->indexVueData['enableDragSort'] ? 140 : 100,
  181. 'render' => 'buttons',
  182. 'buttons' => 'optButtons',
  183. 'operator' => 'false',
  184. ];
  185. if ($this->indexVueData['enableDragSort']) {
  186. array_unshift($this->indexVueData['optButtons'], 'weigh-sort');
  187. }
  188. // 写入语言包代码
  189. Helper::writeWebLangFile($this->langTsData, $webLangDir);
  190. // 写入模型代码
  191. Helper::writeModelFile($tablePk, $fieldsMap, $this->modelData, $modelFile);
  192. // 写入控制器代码
  193. Helper::writeControllerFile($this->controllerData, $controllerFile);
  194. // 写入验证器代码
  195. $validateContent = Helper::assembleStub('mixins/validate/validate', [
  196. 'namespace' => $validateFile['namespace'],
  197. 'className' => $validateFile['lastName'],
  198. ]);
  199. Helper::writeFile($validateFile['parseFile'], $validateContent);
  200. // 写入index.vue代码
  201. $this->indexVueData['tablePk'] = $tablePk;
  202. $this->indexVueData['webTranslate'] = $this->webTranslate;
  203. Helper::writeIndexFile($this->indexVueData, $webViewsDir, $controllerFile);
  204. // 写入form.vue代码
  205. Helper::writeFormFile($this->formVueData, $webViewsDir, $fields, $this->webTranslate);
  206. // 生成菜单
  207. Helper::createMenu($webViewsDir, $tableComment);
  208. Helper::recordCrudStatus([
  209. 'id' => $crudLogId,
  210. 'status' => 'success',
  211. ]);
  212. } catch (PDOException|Exception $e) {
  213. Helper::recordCrudStatus([
  214. 'id' => $crudLogId,
  215. 'status' => 'error',
  216. ]);
  217. if (env('app_debug', false)) throw $e;
  218. $this->error($e->getMessage());
  219. }
  220. $this->success();
  221. }
  222. public function logStart()
  223. {
  224. $id = $this->request->post('id');
  225. $info = CrudLog::find($id)->toArray();
  226. if (!$info) {
  227. $this->error(__('Record not found'));
  228. }
  229. $this->success('', [
  230. 'table' => $info['table'],
  231. 'fields' => $info['fields']
  232. ]);
  233. }
  234. public function delete()
  235. {
  236. $id = $this->request->post('id');
  237. $info = CrudLog::find($id)->toArray();
  238. if (!$info) {
  239. $this->error(__('Record not found'));
  240. }
  241. $webLangDir = Helper::parseWebDirNameData($info['table']['name'], '', 'lang', $info['table']['webViewsDir']);
  242. $files = [
  243. $webLangDir['en'] . '.ts',
  244. $webLangDir['zh-cn'] . '.ts',
  245. $info['table']['webViewsDir'] . '/' . 'index.vue',
  246. $info['table']['webViewsDir'] . '/' . 'popupForm.vue',
  247. $info['table']['controllerFile'],
  248. $info['table']['modelFile'],
  249. $info['table']['validateFile'],
  250. ];
  251. try {
  252. foreach ($files as &$file) {
  253. $file = path_transform(root_path() . $file);
  254. if (file_exists($file)) {
  255. unlink($file);
  256. }
  257. del_empty_dir(dirname($file));
  258. }
  259. // 删除菜单
  260. Menu::delete(Helper::getMenuName($webLangDir), true);
  261. Helper::recordCrudStatus([
  262. 'id' => $id,
  263. 'status' => 'delete',
  264. ]);
  265. } catch (Exception $e) {
  266. $this->error($e->getMessage());
  267. }
  268. $this->success(__('Deleted successfully'));
  269. }
  270. public function getFileData()
  271. {
  272. $table = $this->request->get('table');
  273. if (!$table) {
  274. $this->error(__('Parameter error'));
  275. }
  276. try {
  277. $modelFile = Helper::parseNameData('admin', $table, '', 'model');
  278. $validateFile = Helper::parseNameData('admin', $table, '', 'validate');
  279. $controllerFile = Helper::parseNameData('admin', $table, '', 'controller');
  280. $webViewsDir = Helper::parseWebDirNameData($table, '', 'views');
  281. } catch (Exception $e) {
  282. $this->error($e->getMessage());
  283. }
  284. // 模型和控制器文件和文件列表
  285. $adminModelFiles = get_dir_files(root_path() . 'app' . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR);
  286. $commonModelFiles = get_dir_files(root_path() . 'app' . DIRECTORY_SEPARATOR . 'common' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR);
  287. $adminControllerFiles = get_controller_list();
  288. $modelFileList = [];
  289. $controllerFiles = [];
  290. foreach ($adminModelFiles as $item) {
  291. $item = path_transform('app/admin/model/' . $item);
  292. $modelFileList[$item] = $item;
  293. }
  294. foreach ($commonModelFiles as $item) {
  295. $item = path_transform('app/common/model/' . $item);
  296. $modelFileList[$item] = $item;
  297. }
  298. $outExcludeController = [
  299. 'Addon.php',
  300. 'Ajax.php',
  301. 'Dashboard.php',
  302. 'Index.php',
  303. 'Module.php',
  304. 'Terminal.php',
  305. 'routine/AdminInfo.php',
  306. 'routine/Config.php',
  307. ];
  308. foreach ($adminControllerFiles as $item) {
  309. if (in_array($item, $outExcludeController)) {
  310. continue;
  311. }
  312. $item = path_transform('app/admin/controller/' . $item);
  313. $controllerFiles[$item] = $item;
  314. }
  315. $this->success('', [
  316. 'modelFile' => $modelFile['rootFileName'],
  317. 'controllerFile' => $controllerFile['rootFileName'],
  318. 'validateFile' => $validateFile['rootFileName'],
  319. 'controllerFileList' => $controllerFiles,
  320. 'modelFileList' => $modelFileList,
  321. 'webViewsDir' => $webViewsDir['views'],
  322. ]);
  323. }
  324. public function parseFieldData()
  325. {
  326. $type = $this->request->post('type');
  327. $sql = $this->request->post('sql');
  328. $table = $this->request->post('table');
  329. if ($type == 'db') {
  330. $sql = 'SELECT * FROM `information_schema`.`tables` '
  331. . 'WHERE TABLE_SCHEMA = ? AND table_name = ?';
  332. $tableInfo = Db::query($sql, [config('database.connections.mysql.database'), Helper::getTableName($table)]);
  333. if (!$tableInfo) {
  334. $this->error(__('Record not found'));
  335. }
  336. $this->success('', [
  337. 'columns' => Helper::parseTableColumns($table),
  338. 'comment' => $tableInfo[0]['TABLE_COMMENT'] ?? '',
  339. ]);
  340. } elseif ($type == 'sql') {
  341. // TODO
  342. }
  343. }
  344. public function generateCheck()
  345. {
  346. $table = $this->request->post('table');
  347. $controllerFile = $this->request->post('controllerFile', '');
  348. if (!$table) {
  349. $this->error(__('Parameter error'));
  350. }
  351. try {
  352. if (!$controllerFile) {
  353. $controllerFile = Helper::parseNameData('admin', $table, '', 'controller')['rootFileName'];
  354. }
  355. } catch (Exception $e) {
  356. $this->error($e->getMessage());
  357. }
  358. $tableList = get_table_list();
  359. $tableExist = array_key_exists(Helper::getTableName($table), $tableList);
  360. $controllerExist = file_exists(root_path() . $controllerFile);
  361. if ($controllerExist || $tableExist) {
  362. $this->error('', [
  363. 'table' => $tableExist,
  364. 'controller' => $controllerExist,
  365. ], -1);
  366. }
  367. $this->success();
  368. }
  369. public function databaseList()
  370. {
  371. $tablePrefix = config('database.connections.mysql.prefix');
  372. $outExcludeTable = [
  373. // 功能表
  374. 'area',
  375. 'token',
  376. 'captcha',
  377. 'admin_group_access',
  378. 'config',
  379. 'admin_log',
  380. // 不建议生成crud的表
  381. 'user_money_log',
  382. 'user_score_log',
  383. ];
  384. $outTables = [];
  385. $tables = get_table_list();
  386. $pattern = '/^' . $tablePrefix . '/i';
  387. foreach ($tables as $table => $tableComment) {
  388. $table = preg_replace($pattern, '', $table);
  389. if (!in_array($table, $outExcludeTable)) {
  390. $outTables[$table] = $tableComment;
  391. }
  392. }
  393. $this->success('', [
  394. 'dbs' => $outTables,
  395. ]);
  396. }
  397. /**
  398. * 关联表数据解析
  399. * @param $field
  400. * @throws Exception
  401. */
  402. private function parseJoinData($field)
  403. {
  404. $dictEn = [];
  405. $dictZhCn = [];
  406. if ($field['form']['relation-fields'] && $field['form']['remote-table']) {
  407. $columns = Helper::parseTableColumns($field['form']['remote-table'], true);
  408. $relationFields = explode(',', $field['form']['relation-fields']);
  409. $tableName = Helper::getTableName($field['form']['remote-table'], false);
  410. $relationMethod = parse_name($tableName, 1, false);
  411. // 建立关联模型代码文件
  412. if (!$field['form']['remote-model'] || !file_exists(root_path() . $field['form']['remote-model'])) {
  413. $joinModelFile = Helper::parseNameData('admin', $tableName, '', 'model', $field['form']['remote-model']);
  414. if (!file_exists(root_path() . $joinModelFile['rootFileName'])) {
  415. $joinModelData['append'] = [];
  416. $joinModelData['methods'] = [];
  417. $joinModelData['fieldType'] = [];
  418. $joinModelData['createTime'] = '';
  419. $joinModelData['updateTime'] = '';
  420. $joinModelData['beforeInsertMixins'] = [];
  421. $joinModelData['beforeInsert'] = '';
  422. $joinModelData['afterInsert'] = '';
  423. $joinModelData['name'] = $tableName;
  424. $joinModelData['className'] = $joinModelFile['lastName'];
  425. $joinModelData['namespace'] = $joinModelFile['namespace'];
  426. $joinTablePk = 'id';
  427. $joinFieldsMap = [];
  428. foreach ($columns as $column) {
  429. $joinFieldsMap[$column['name']] = $column['designType'];
  430. $this->parseModelMethods($column, $joinModelData);
  431. if ($column['primaryKey']) $joinTablePk = $column['name'];
  432. }
  433. $weighKey = array_search('weigh', $joinFieldsMap);
  434. if ($weighKey !== false) {
  435. $joinModelData['afterInsert'] = Helper::assembleStub('mixins/model/afterInsert', [
  436. 'field' => $joinFieldsMap[$weighKey]
  437. ]);
  438. }
  439. Helper::writeModelFile($joinTablePk, $joinFieldsMap, $joinModelData, $joinModelFile);
  440. }
  441. $field['form']['remote-model'] = $joinModelFile['rootFileName'];
  442. }
  443. if ($field['designType'] == 'remoteSelect') {
  444. // 关联预载入方法
  445. $this->controllerData['attr']['withJoinTable'][$tableName] = $relationMethod;
  446. // 模型方法代码
  447. $relationData = [
  448. 'relationMethod' => $relationMethod,
  449. 'relationMode' => 'belongsTo',
  450. 'relationPrimaryKey' => $field['form']['remote-pk'] ?? 'id',
  451. 'relationForeignKey' => $field['name'],
  452. 'relationClassName' => str_replace(['.php', '/'], ['', '\\'], '\\' . $field['form']['remote-model']) . "::class",
  453. ];
  454. $this->modelData['relationMethodList'][$tableName] = Helper::assembleStub('mixins/model/belongsTo', $relationData);
  455. // 查询时显示的字段
  456. if ($relationFields) {
  457. $this->controllerData['relationVisibleFieldList'][$relationData['relationMethod']] = $relationFields;
  458. }
  459. } elseif ($field['designType'] == 'remoteSelects') {
  460. $this->modelData['append'][] = parse_name($tableName, 1, false);
  461. $this->modelData['methods'][] = Helper::assembleStub('mixins/model/getters/remoteSelectLabels', [
  462. 'field' => parse_name($tableName, 1),
  463. 'className' => str_replace(['.php', '/'], ['', '\\'], '\\' . $field['form']['remote-model']),
  464. 'primaryKey' => $field['form']['remote-pk'] ?? 'id',
  465. 'foreignKey' => $field['name'],
  466. 'labelFieldName' => $field['form']['remote-field'] ?? 'name',
  467. ]);
  468. }
  469. foreach ($relationFields as $relationField) {
  470. $relationFieldPrefix = $relationMethod . '.';
  471. $relationFieldLangPrefix = strtolower($tableName) . '__';
  472. if (array_key_exists($relationField, $columns)) {
  473. Helper::getDictData($dictEn, $columns[$relationField], 'en', $relationFieldLangPrefix);
  474. Helper::getDictData($dictZhCn, $columns[$relationField], 'zh-cn', $relationFieldLangPrefix);
  475. }
  476. // 不允许双击编辑的字段
  477. if ($columns[$relationField]['designType'] == 'switch') {
  478. $this->indexVueData['dblClickNotEditColumn'][] = $field['name'];
  479. }
  480. // 列字典数据
  481. $columnDict = $this->getColumnDict($columns[$relationField], $relationFieldLangPrefix);
  482. // 表格列
  483. $columns[$relationField]['table']['render'] = 'tags';
  484. $columns[$relationField]['table']['operator'] = 'LIKE';
  485. $columns[$relationField]['designType'] = $field['designType'];
  486. $this->indexVueData['tableColumn'][] = $this->getTableColumn($columns[$relationField], $columnDict, $relationFieldPrefix, $relationFieldLangPrefix);
  487. }
  488. }
  489. $this->langTsData['en'] = array_merge($this->langTsData['en'], $dictEn);
  490. $this->langTsData['zh-cn'] = array_merge($this->langTsData['zh-cn'], $dictZhCn);
  491. }
  492. /**
  493. * 解析模型方法(设置器、获取器等)
  494. */
  495. private function parseModelMethods($field, &$modelData)
  496. {
  497. // fieldType
  498. if ($field['designType'] == 'array') {
  499. $modelData['fieldType'][$field['name']] = 'json';
  500. } elseif (!in_array($field['name'], ['create_time', 'update_time', 'updatetime', 'createtime']) && $field['designType'] == 'datetime' && (in_array($field['type'], ['int', 'bigint']))) {
  501. $modelData['fieldType'][$field['name']] = 'timestamp:Y-m-d H:i:s';
  502. }
  503. // beforeInsertMixins
  504. if ($field['designType'] == 'spk') {
  505. $modelData['beforeInsertMixins']['snowflake'] = Helper::assembleStub('mixins/model/mixins/beforeInsertWithSnowflake', []);
  506. }
  507. // methods
  508. $fieldName = parse_name($field['name'], 1);
  509. if (in_array($field['designType'], $this->dtStringToArray)) {
  510. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/stringToArray', [
  511. 'field' => $fieldName
  512. ]);
  513. $modelData['methods'][] = Helper::assembleStub('mixins/model/setters/arrayToString', [
  514. 'field' => $fieldName
  515. ]);
  516. } elseif ($field['designType'] == 'array') {
  517. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/jsonDecode', [
  518. 'field' => $fieldName
  519. ]);
  520. } elseif ($field['designType'] == 'time') {
  521. $modelData['methods'][] = Helper::assembleStub('mixins/model/setters/time', [
  522. 'field' => $fieldName
  523. ]);
  524. } elseif ($field['designType'] == 'editor') {
  525. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/htmlDecode', [
  526. 'field' => $fieldName
  527. ]);
  528. } elseif ($field['designType'] == 'spk') {
  529. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/string', [
  530. 'field' => $fieldName
  531. ]);
  532. } elseif ($field['originalDesignType'] == 'float') {
  533. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/float', [
  534. 'field' => $fieldName
  535. ]);
  536. }
  537. if ($field['designType'] == 'city') {
  538. $modelData['append'][] = $field['name'] . '_text';
  539. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/cityNames', [
  540. 'field' => $fieldName . 'Text',
  541. 'originalFieldName' => $field['name'],
  542. ]);
  543. }
  544. }
  545. /**
  546. * 控制器/模型等文件的一些杂项属性解析
  547. */
  548. private function parseSundryData($field, $table)
  549. {
  550. if ($field['designType'] == 'editor') {
  551. $this->formVueData['bigDialog'] = 'true'; // form 使用较宽的 Dialog
  552. $this->controllerData['filterRule'] = "\n" . Helper::tab(2) . '$this->request->filter(\'trim,htmlspecialchars\');';// 修改变量过滤规则
  553. }
  554. // 默认排序字段
  555. if ($table['defaultSortField'] && $table['defaultSortType']) {
  556. $defaultSortField = "{$table['defaultSortField']},{$table['defaultSortType']}";
  557. if ($defaultSortField == 'id,desc') {
  558. $this->controllerData['attr']['defaultSortField'] = '';
  559. } else {
  560. $this->controllerData['attr']['defaultSortField'] = $defaultSortField;
  561. $this->indexVueData['defaultOrder'] = Helper::buildDefaultOrder($table['defaultSortField'], $table['defaultSortType']);
  562. }
  563. }
  564. }
  565. private function getFormField($field, $columnDict): array
  566. {
  567. // 表单项属性
  568. $formField = [
  569. ':label' => 't(\'' . $this->webTranslate . $field['name'] . '\')',
  570. 'type' => $field['designType'],
  571. 'v-model' => 'baTable.form.items!.' . $field['name'],
  572. 'prop' => $field['name'],
  573. ];
  574. // 不同输入框的属性处理
  575. if ($columnDict || in_array($field['designType'], ['radio', 'checkbox', 'select', 'selects'])) {
  576. $formField[':data'] = [
  577. 'content' => $columnDict,
  578. ];
  579. } elseif ($field['designType'] == 'textarea') {
  580. $formField[':input-attr']['rows'] = (int)($field['form']['rows'] ?? 3);
  581. $formField['@keyup.enter.stop'] = '';
  582. $formField['@keyup.ctrl.enter'] = 'baTable.onSubmit(formRef)';
  583. } elseif ($field['designType'] == 'remoteSelect' || $field['designType'] == 'remoteSelects') {
  584. $formField[':input-attr']['pk'] = Helper::getTableName($field['form']['remote-table'], false) . '.' . ($field['form']['remote-pk'] ?? 'id');
  585. $formField[':input-attr']['field'] = $field['form']['remote-field'] ?? 'name';
  586. $formField[':input-attr']['remote-url'] = $this->getRemoteSelectUrl($field);
  587. if ($field['designType'] == 'remoteSelects') {
  588. $formField['type'] = 'remoteSelect';
  589. $formField[':input-attr']['multiple'] = 'true';
  590. }
  591. } elseif ($field['designType'] == 'number') {
  592. $formField[':input-attr']['step'] = (int)($field['form']['step'] ?? 1);
  593. $formField['v-model.number'] = $formField['v-model'];
  594. unset($formField['v-model']);
  595. } elseif ($field['designType'] == 'icon') {
  596. $formField[':input-attr']['placement'] = 'top';
  597. } elseif ($field['designType'] == 'editor') {
  598. $formField['@keyup.enter.stop'] = '';
  599. $formField['@keyup.ctrl.enter'] = 'baTable.onSubmit(formRef)';
  600. }
  601. // placeholder
  602. if (!in_array($field['designType'], ['image', 'images', 'file', 'files', 'switch'])) {
  603. if (in_array($field['designType'], ['radio', 'checkbox', 'datetime', 'year', 'date', 'time', 'select', 'selects', 'remoteSelect', 'remoteSelects', 'city', 'icon'])) {
  604. $formField[':placeholder'] = "t('Please select field', { field: t('" . $this->webTranslate . $field['name'] . "') })";
  605. } else {
  606. $formField[':placeholder'] = "t('Please input field', { field: t('" . $this->webTranslate . $field['name'] . "') })";
  607. }
  608. }
  609. // 默认值
  610. if ($field['default'] && $field['default'] != 'empty string') {
  611. $this->indexVueData['defaultItems'][$field['name']] = $field['default'];
  612. }
  613. if ($field['default'] == 'null') {
  614. $this->indexVueData['defaultItems'][$field['name']] = null;
  615. } elseif ($field['default'] == '0' && in_array($field['designType'], ['radio', 'checkbox', 'select', 'selects'])) {
  616. // 防止为`0`时无法设置上默认值
  617. $this->indexVueData['defaultItems'][$field['name']] = '0';
  618. }
  619. if ($field['designType'] == 'array') {
  620. $this->indexVueData['defaultItems'][$field['name']] = "[]";
  621. } elseif (in_array($field['designType'], $this->dtStringToArray) && stripos($field['default'], ',') !== false) {
  622. $this->indexVueData['defaultItems'][$field['name']] = Helper::buildSimpleArray(explode(',', $field['default']));
  623. } elseif (in_array($field['designType'], ['weigh', 'number', 'float'])) {
  624. $this->indexVueData['defaultItems'][$field['name']] = (float)$field['default'];
  625. }
  626. return $formField;
  627. }
  628. private function getRemoteSelectUrl($field): string
  629. {
  630. if ($field['form']['remote-url']) return $field['form']['remote-url'];
  631. $url = '';
  632. if ($field['form']['remote-controller']) {
  633. $pathArr = [];
  634. $controller = explode(DIRECTORY_SEPARATOR, $field['form']['remote-controller']);
  635. $controller = str_replace('.php', '', $controller);
  636. $redundantDir = [
  637. 'app' => 0,
  638. 'admin' => 1,
  639. 'controller' => 2,
  640. ];
  641. foreach ($controller as $key => $item) {
  642. if (!array_key_exists($item, $redundantDir) || $key !== $redundantDir[$item]) {
  643. $pathArr[] = $item;
  644. }
  645. }
  646. $url = count($pathArr) > 1 ? implode('.', $pathArr) : $pathArr[0];
  647. $url = '/admin/' . $url . '/index';
  648. }
  649. return $url;
  650. }
  651. private function getTableColumn($field, $columnDict, $fieldNamePrefix = '', $translationPrefix = ''): array
  652. {
  653. $column = [
  654. 'label' => "t('" . $this->webTranslate . $translationPrefix . $field['name'] . "')",
  655. 'prop' => $fieldNamePrefix . $field['name'] . ($field['designType'] == 'city' ? '_text' : ''),
  656. 'align' => 'center',
  657. ];
  658. // 模糊搜索增加一个placeholder
  659. if (isset($field['table']['operator']) && $field['table']['operator'] == 'LIKE') {
  660. $column['operatorPlaceholder'] = "t('Fuzzy query')";
  661. }
  662. // 合并前端预设的字段表格属性
  663. if (isset($field['table']) && $field['table']) {
  664. $column = array_merge($column, $field['table']);
  665. }
  666. // 需要值替换的渲染类型
  667. $columnReplaceValue = ['tag', 'tags', 'switch'];
  668. if (!in_array($field['designType'], ['remoteSelect', 'remoteSelects']) && ($columnDict || (isset($field['table']['render']) && in_array($field['table']['render'], $columnReplaceValue)))) {
  669. $column['replaceValue'] = $columnDict;
  670. }
  671. if (isset($column['render']) && $column['render'] == 'none') {
  672. unset($column['render']);
  673. }
  674. return $column;
  675. }
  676. private function getColumnDict($column, $translationPrefix = ''): array
  677. {
  678. $dict = [];
  679. // 确保字典中无翻译也可以识别到该值
  680. if (in_array($column['type'], ['enum', 'set'])) {
  681. $dataType = str_replace(' ', '', $column['dataType']);
  682. $columnData = substr($dataType, stripos($dataType, '(') + 1, -1);
  683. $columnData = explode(',', str_replace(["'", '"'], '', $columnData));
  684. foreach ($columnData as $columnDatum) {
  685. $dict[$columnDatum] = $column['name'] . ' ' . $columnDatum;
  686. }
  687. }
  688. $dictData = [];
  689. Helper::getDictData($dictData, $column, 'zh-cn', $translationPrefix);
  690. if ($dictData) {
  691. unset($dictData[$translationPrefix . $column['name']]);
  692. foreach ($dictData as $key => $item) {
  693. $keyName = str_replace($translationPrefix . $column['name'] . ' ', '', $key);
  694. $dict[$keyName] = "t('" . $this->webTranslate . $key . "')";
  695. }
  696. }
  697. return $dict;
  698. }
  699. }