Crud.php 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  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($table['isCommonModel'] ? 'common' : '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. $commonModel = $this->request->get('commonModel/b');
  274. if (!$table) {
  275. $this->error(__('Parameter error'));
  276. }
  277. try {
  278. $modelFile = Helper::parseNameData($commonModel ? 'common' : 'admin', $table, 'model');
  279. $validateFile = Helper::parseNameData('admin', $table, 'validate');
  280. $controllerFile = Helper::parseNameData('admin', $table, 'controller');
  281. $webViewsDir = Helper::parseWebDirNameData($table, 'views');
  282. } catch (Exception $e) {
  283. $this->error($e->getMessage());
  284. }
  285. // 模型和控制器文件和文件列表
  286. $adminModelFiles = get_dir_files(root_path() . 'app' . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR);
  287. $commonModelFiles = get_dir_files(root_path() . 'app' . DIRECTORY_SEPARATOR . 'common' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR);
  288. $adminControllerFiles = get_controller_list();
  289. $modelFileList = [];
  290. $controllerFiles = [];
  291. foreach ($adminModelFiles as $item) {
  292. $item = path_transform('app/admin/model/' . $item);
  293. $modelFileList[$item] = $item;
  294. }
  295. foreach ($commonModelFiles as $item) {
  296. $item = path_transform('app/common/model/' . $item);
  297. $modelFileList[$item] = $item;
  298. }
  299. $outExcludeController = [
  300. 'Addon.php',
  301. 'Ajax.php',
  302. 'Dashboard.php',
  303. 'Index.php',
  304. 'Module.php',
  305. 'Terminal.php',
  306. 'routine/AdminInfo.php',
  307. 'routine/Config.php',
  308. ];
  309. foreach ($adminControllerFiles as $item) {
  310. if (in_array($item, $outExcludeController)) {
  311. continue;
  312. }
  313. $item = path_transform('app/admin/controller/' . $item);
  314. $controllerFiles[$item] = $item;
  315. }
  316. $this->success('', [
  317. 'modelFile' => $modelFile['rootFileName'],
  318. 'controllerFile' => $controllerFile['rootFileName'],
  319. 'validateFile' => $validateFile['rootFileName'],
  320. 'controllerFileList' => $controllerFiles,
  321. 'modelFileList' => $modelFileList,
  322. 'webViewsDir' => $webViewsDir['views'],
  323. ]);
  324. }
  325. public function parseFieldData()
  326. {
  327. $type = $this->request->post('type');
  328. $sql = $this->request->post('sql');
  329. $table = $this->request->post('table');
  330. if ($type == 'db') {
  331. $sql = 'SELECT * FROM `information_schema`.`tables` '
  332. . 'WHERE TABLE_SCHEMA = ? AND table_name = ?';
  333. $tableInfo = Db::query($sql, [config('database.connections.mysql.database'), Helper::getTableName($table)]);
  334. if (!$tableInfo) {
  335. $this->error(__('Record not found'));
  336. }
  337. $this->success('', [
  338. 'columns' => Helper::parseTableColumns($table),
  339. 'comment' => $tableInfo[0]['TABLE_COMMENT'] ?? '',
  340. ]);
  341. } elseif ($type == 'sql') {
  342. // TODO
  343. }
  344. }
  345. public function generateCheck()
  346. {
  347. $table = $this->request->post('table');
  348. $controllerFile = $this->request->post('controllerFile', '');
  349. if (!$table) {
  350. $this->error(__('Parameter error'));
  351. }
  352. try {
  353. if (!$controllerFile) {
  354. $controllerFile = Helper::parseNameData('admin', $table, 'controller')['rootFileName'];
  355. }
  356. } catch (Exception $e) {
  357. $this->error($e->getMessage());
  358. }
  359. $tableList = get_table_list();
  360. $tableExist = array_key_exists(Helper::getTableName($table), $tableList);
  361. $controllerExist = file_exists(root_path() . $controllerFile);
  362. if ($controllerExist || $tableExist) {
  363. $this->error('', [
  364. 'table' => $tableExist,
  365. 'controller' => $controllerExist,
  366. ], -1);
  367. }
  368. $this->success();
  369. }
  370. public function databaseList()
  371. {
  372. $tablePrefix = config('database.connections.mysql.prefix');
  373. $outExcludeTable = [
  374. // 功能表
  375. 'area',
  376. 'token',
  377. 'captcha',
  378. 'admin_group_access',
  379. 'config',
  380. 'admin_log',
  381. // 不建议生成crud的表
  382. 'user_money_log',
  383. 'user_score_log',
  384. ];
  385. $outTables = [];
  386. $tables = get_table_list();
  387. $pattern = '/^' . $tablePrefix . '/i';
  388. foreach ($tables as $table => $tableComment) {
  389. if (!preg_match($pattern, $table)) continue;
  390. $table = preg_replace($pattern, '', $table);
  391. if (!in_array($table, $outExcludeTable)) {
  392. $outTables[$table] = $tableComment;
  393. }
  394. }
  395. $this->success('', [
  396. 'dbs' => $outTables,
  397. ]);
  398. }
  399. /**
  400. * 关联表数据解析
  401. * @param $field
  402. * @throws Exception
  403. */
  404. private function parseJoinData($field)
  405. {
  406. $dictEn = [];
  407. $dictZhCn = [];
  408. if ($field['form']['relation-fields'] && $field['form']['remote-table']) {
  409. $columns = Helper::parseTableColumns($field['form']['remote-table'], true);
  410. $relationFields = explode(',', $field['form']['relation-fields']);
  411. $tableName = Helper::getTableName($field['form']['remote-table'], false);
  412. $relationMethod = parse_name($tableName, 1, false);
  413. // 建立关联模型代码文件
  414. if (!$field['form']['remote-model'] || !file_exists(root_path() . $field['form']['remote-model'])) {
  415. $joinModelFile = Helper::parseNameData('admin', $tableName, 'model', $field['form']['remote-model']);
  416. if (!file_exists(root_path() . $joinModelFile['rootFileName'])) {
  417. $joinModelData['append'] = [];
  418. $joinModelData['methods'] = [];
  419. $joinModelData['fieldType'] = [];
  420. $joinModelData['createTime'] = '';
  421. $joinModelData['updateTime'] = '';
  422. $joinModelData['beforeInsertMixins'] = [];
  423. $joinModelData['beforeInsert'] = '';
  424. $joinModelData['afterInsert'] = '';
  425. $joinModelData['name'] = $tableName;
  426. $joinModelData['className'] = $joinModelFile['lastName'];
  427. $joinModelData['namespace'] = $joinModelFile['namespace'];
  428. $joinTablePk = 'id';
  429. $joinFieldsMap = [];
  430. foreach ($columns as $column) {
  431. $joinFieldsMap[$column['name']] = $column['designType'];
  432. $this->parseModelMethods($column, $joinModelData);
  433. if ($column['primaryKey']) $joinTablePk = $column['name'];
  434. }
  435. $weighKey = array_search('weigh', $joinFieldsMap);
  436. if ($weighKey !== false) {
  437. $joinModelData['afterInsert'] = Helper::assembleStub('mixins/model/afterInsert', [
  438. 'field' => $joinFieldsMap[$weighKey]
  439. ]);
  440. }
  441. Helper::writeModelFile($joinTablePk, $joinFieldsMap, $joinModelData, $joinModelFile);
  442. }
  443. $field['form']['remote-model'] = $joinModelFile['rootFileName'];
  444. }
  445. if ($field['designType'] == 'remoteSelect') {
  446. // 关联预载入方法
  447. $this->controllerData['attr']['withJoinTable'][$tableName] = $relationMethod;
  448. // 模型方法代码
  449. $relationData = [
  450. 'relationMethod' => $relationMethod,
  451. 'relationMode' => 'belongsTo',
  452. 'relationPrimaryKey' => $field['form']['remote-pk'] ?? 'id',
  453. 'relationForeignKey' => $field['name'],
  454. 'relationClassName' => str_replace(['.php', '/'], ['', '\\'], '\\' . $field['form']['remote-model']) . "::class",
  455. ];
  456. $this->modelData['relationMethodList'][$tableName] = Helper::assembleStub('mixins/model/belongsTo', $relationData);
  457. // 查询时显示的字段
  458. if ($relationFields) {
  459. $this->controllerData['relationVisibleFieldList'][$relationData['relationMethod']] = $relationFields;
  460. }
  461. } elseif ($field['designType'] == 'remoteSelects') {
  462. $this->modelData['append'][] = parse_name($tableName, 1, false);
  463. $this->modelData['methods'][] = Helper::assembleStub('mixins/model/getters/remoteSelectLabels', [
  464. 'field' => parse_name($tableName, 1),
  465. 'className' => str_replace(['.php', '/'], ['', '\\'], '\\' . $field['form']['remote-model']),
  466. 'primaryKey' => $field['form']['remote-pk'] ?? 'id',
  467. 'foreignKey' => $field['name'],
  468. 'labelFieldName' => $field['form']['remote-field'] ?? 'name',
  469. ]);
  470. }
  471. foreach ($relationFields as $relationField) {
  472. $relationFieldPrefix = $relationMethod . '.';
  473. $relationFieldLangPrefix = strtolower($tableName) . '__';
  474. if (array_key_exists($relationField, $columns)) {
  475. Helper::getDictData($dictEn, $columns[$relationField], 'en', $relationFieldLangPrefix);
  476. Helper::getDictData($dictZhCn, $columns[$relationField], 'zh-cn', $relationFieldLangPrefix);
  477. }
  478. // 不允许双击编辑的字段
  479. if ($columns[$relationField]['designType'] == 'switch') {
  480. $this->indexVueData['dblClickNotEditColumn'][] = $field['name'];
  481. }
  482. // 列字典数据
  483. $columnDict = $this->getColumnDict($columns[$relationField], $relationFieldLangPrefix);
  484. // 表格列
  485. $columns[$relationField]['table']['render'] = 'tags';
  486. $columns[$relationField]['table']['operator'] = 'LIKE';
  487. $columns[$relationField]['designType'] = $field['designType'];
  488. $this->indexVueData['tableColumn'][] = $this->getTableColumn($columns[$relationField], $columnDict, $relationFieldPrefix, $relationFieldLangPrefix);
  489. }
  490. }
  491. $this->langTsData['en'] = array_merge($this->langTsData['en'], $dictEn);
  492. $this->langTsData['zh-cn'] = array_merge($this->langTsData['zh-cn'], $dictZhCn);
  493. }
  494. /**
  495. * 解析模型方法(设置器、获取器等)
  496. */
  497. private function parseModelMethods($field, &$modelData)
  498. {
  499. // fieldType
  500. if ($field['designType'] == 'array') {
  501. $modelData['fieldType'][$field['name']] = 'json';
  502. } elseif (!in_array($field['name'], ['create_time', 'update_time', 'updatetime', 'createtime']) && $field['designType'] == 'datetime' && (in_array($field['type'], ['int', 'bigint']))) {
  503. $modelData['fieldType'][$field['name']] = 'timestamp:Y-m-d H:i:s';
  504. }
  505. // beforeInsertMixins
  506. if ($field['designType'] == 'spk') {
  507. $modelData['beforeInsertMixins']['snowflake'] = Helper::assembleStub('mixins/model/mixins/beforeInsertWithSnowflake', []);
  508. }
  509. // methods
  510. $fieldName = parse_name($field['name'], 1);
  511. if (in_array($field['designType'], $this->dtStringToArray)) {
  512. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/stringToArray', [
  513. 'field' => $fieldName
  514. ]);
  515. $modelData['methods'][] = Helper::assembleStub('mixins/model/setters/arrayToString', [
  516. 'field' => $fieldName
  517. ]);
  518. } elseif ($field['designType'] == 'array') {
  519. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/jsonDecode', [
  520. 'field' => $fieldName
  521. ]);
  522. } elseif ($field['designType'] == 'time') {
  523. $modelData['methods'][] = Helper::assembleStub('mixins/model/setters/time', [
  524. 'field' => $fieldName
  525. ]);
  526. } elseif ($field['designType'] == 'editor') {
  527. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/htmlDecode', [
  528. 'field' => $fieldName
  529. ]);
  530. } elseif ($field['designType'] == 'spk') {
  531. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/string', [
  532. 'field' => $fieldName
  533. ]);
  534. } elseif ($field['originalDesignType'] == 'float') {
  535. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/float', [
  536. 'field' => $fieldName
  537. ]);
  538. }
  539. if ($field['designType'] == 'city') {
  540. $modelData['append'][] = $field['name'] . '_text';
  541. $modelData['methods'][] = Helper::assembleStub('mixins/model/getters/cityNames', [
  542. 'field' => $fieldName . 'Text',
  543. 'originalFieldName' => $field['name'],
  544. ]);
  545. }
  546. }
  547. /**
  548. * 控制器/模型等文件的一些杂项属性解析
  549. */
  550. private function parseSundryData($field, $table)
  551. {
  552. if ($field['designType'] == 'editor') {
  553. $this->formVueData['bigDialog'] = 'true'; // form 使用较宽的 Dialog
  554. $this->controllerData['filterRule'] = "\n" . Helper::tab(2) . '$this->request->filter(\'trim,htmlspecialchars\');';// 修改变量过滤规则
  555. }
  556. // 默认排序字段
  557. if ($table['defaultSortField'] && $table['defaultSortType']) {
  558. $defaultSortField = "{$table['defaultSortField']},{$table['defaultSortType']}";
  559. if ($defaultSortField == 'id,desc') {
  560. $this->controllerData['attr']['defaultSortField'] = '';
  561. } else {
  562. $this->controllerData['attr']['defaultSortField'] = $defaultSortField;
  563. $this->indexVueData['defaultOrder'] = Helper::buildDefaultOrder($table['defaultSortField'], $table['defaultSortType']);
  564. }
  565. }
  566. }
  567. private function getFormField($field, $columnDict): array
  568. {
  569. // 表单项属性
  570. $formField = [
  571. ':label' => 't(\'' . $this->webTranslate . $field['name'] . '\')',
  572. 'type' => $field['designType'],
  573. 'v-model' => 'baTable.form.items!.' . $field['name'],
  574. 'prop' => $field['name'],
  575. ];
  576. // 不同输入框的属性处理
  577. if ($columnDict || in_array($field['designType'], ['radio', 'checkbox', 'select', 'selects'])) {
  578. $formField[':data'] = [
  579. 'content' => $columnDict,
  580. ];
  581. } elseif ($field['designType'] == 'textarea') {
  582. $formField[':input-attr']['rows'] = (int)($field['form']['rows'] ?? 3);
  583. $formField['@keyup.enter.stop'] = '';
  584. $formField['@keyup.ctrl.enter'] = 'baTable.onSubmit(formRef)';
  585. } elseif ($field['designType'] == 'remoteSelect' || $field['designType'] == 'remoteSelects') {
  586. $formField[':input-attr']['pk'] = Helper::getTableName($field['form']['remote-table'], false) . '.' . ($field['form']['remote-pk'] ?? 'id');
  587. $formField[':input-attr']['field'] = $field['form']['remote-field'] ?? 'name';
  588. $formField[':input-attr']['remote-url'] = $this->getRemoteSelectUrl($field);
  589. if ($field['designType'] == 'remoteSelects') {
  590. $formField['type'] = 'remoteSelect';
  591. $formField[':input-attr']['multiple'] = 'true';
  592. }
  593. } elseif ($field['designType'] == 'number') {
  594. $formField[':input-attr']['step'] = (int)($field['form']['step'] ?? 1);
  595. $formField['v-model.number'] = $formField['v-model'];
  596. unset($formField['v-model']);
  597. } elseif ($field['designType'] == 'icon') {
  598. $formField[':input-attr']['placement'] = 'top';
  599. } elseif ($field['designType'] == 'editor') {
  600. $formField['@keyup.enter.stop'] = '';
  601. $formField['@keyup.ctrl.enter'] = 'baTable.onSubmit(formRef)';
  602. }
  603. // placeholder
  604. if (!in_array($field['designType'], ['image', 'images', 'file', 'files', 'switch'])) {
  605. if (in_array($field['designType'], ['radio', 'checkbox', 'datetime', 'year', 'date', 'time', 'select', 'selects', 'remoteSelect', 'remoteSelects', 'city', 'icon'])) {
  606. $formField[':placeholder'] = "t('Please select field', { field: t('" . $this->webTranslate . $field['name'] . "') })";
  607. } else {
  608. $formField[':placeholder'] = "t('Please input field', { field: t('" . $this->webTranslate . $field['name'] . "') })";
  609. }
  610. }
  611. // 默认值
  612. if ($field['default'] && $field['default'] != 'empty string') {
  613. $this->indexVueData['defaultItems'][$field['name']] = $field['default'];
  614. }
  615. if ($field['default'] == 'null') {
  616. $this->indexVueData['defaultItems'][$field['name']] = null;
  617. } elseif ($field['default'] == '0' && in_array($field['designType'], ['radio', 'checkbox', 'select', 'selects'])) {
  618. // 防止为`0`时无法设置上默认值
  619. $this->indexVueData['defaultItems'][$field['name']] = '0';
  620. }
  621. if ($field['designType'] == 'array') {
  622. $this->indexVueData['defaultItems'][$field['name']] = "[]";
  623. } elseif (in_array($field['designType'], $this->dtStringToArray) && stripos($field['default'], ',') !== false) {
  624. $this->indexVueData['defaultItems'][$field['name']] = Helper::buildSimpleArray(explode(',', $field['default']));
  625. } elseif (in_array($field['designType'], ['weigh', 'number', 'float'])) {
  626. $this->indexVueData['defaultItems'][$field['name']] = (float)$field['default'];
  627. }
  628. return $formField;
  629. }
  630. private function getRemoteSelectUrl($field): string
  631. {
  632. if ($field['form']['remote-url']) return $field['form']['remote-url'];
  633. $url = '';
  634. if ($field['form']['remote-controller']) {
  635. $pathArr = [];
  636. $controller = explode(DIRECTORY_SEPARATOR, $field['form']['remote-controller']);
  637. $controller = str_replace('.php', '', $controller);
  638. $redundantDir = [
  639. 'app' => 0,
  640. 'admin' => 1,
  641. 'controller' => 2,
  642. ];
  643. foreach ($controller as $key => $item) {
  644. if (!array_key_exists($item, $redundantDir) || $key !== $redundantDir[$item]) {
  645. $pathArr[] = $item;
  646. }
  647. }
  648. $url = count($pathArr) > 1 ? implode('.', $pathArr) : $pathArr[0];
  649. $url = '/admin/' . $url . '/index';
  650. }
  651. return $url;
  652. }
  653. private function getTableColumn($field, $columnDict, $fieldNamePrefix = '', $translationPrefix = ''): array
  654. {
  655. $column = [
  656. 'label' => "t('" . $this->webTranslate . $translationPrefix . $field['name'] . "')",
  657. 'prop' => $fieldNamePrefix . $field['name'] . ($field['designType'] == 'city' ? '_text' : ''),
  658. 'align' => 'center',
  659. ];
  660. // 模糊搜索增加一个placeholder
  661. if (isset($field['table']['operator']) && $field['table']['operator'] == 'LIKE') {
  662. $column['operatorPlaceholder'] = "t('Fuzzy query')";
  663. }
  664. // 合并前端预设的字段表格属性
  665. if (isset($field['table']) && $field['table']) {
  666. $column = array_merge($column, $field['table']);
  667. }
  668. // 需要值替换的渲染类型
  669. $columnReplaceValue = ['tag', 'tags', 'switch'];
  670. if (!in_array($field['designType'], ['remoteSelect', 'remoteSelects']) && ($columnDict || (isset($field['table']['render']) && in_array($field['table']['render'], $columnReplaceValue)))) {
  671. $column['replaceValue'] = $columnDict;
  672. }
  673. if (isset($column['render']) && $column['render'] == 'none') {
  674. unset($column['render']);
  675. }
  676. return $column;
  677. }
  678. private function getColumnDict($column, $translationPrefix = ''): array
  679. {
  680. $dict = [];
  681. // 确保字典中无翻译也可以识别到该值
  682. if (in_array($column['type'], ['enum', 'set'])) {
  683. $dataType = str_replace(' ', '', $column['dataType']);
  684. $columnData = substr($dataType, stripos($dataType, '(') + 1, -1);
  685. $columnData = explode(',', str_replace(["'", '"'], '', $columnData));
  686. foreach ($columnData as $columnDatum) {
  687. $dict[$columnDatum] = $column['name'] . ' ' . $columnDatum;
  688. }
  689. }
  690. $dictData = [];
  691. Helper::getDictData($dictData, $column, 'zh-cn', $translationPrefix);
  692. if ($dictData) {
  693. unset($dictData[$translationPrefix . $column['name']]);
  694. foreach ($dictData as $key => $item) {
  695. $keyName = str_replace($translationPrefix . $column['name'] . ' ', '', $key);
  696. $dict[$keyName] = "t('" . $this->webTranslate . $key . "')";
  697. }
  698. }
  699. return $dict;
  700. }
  701. }