1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546 |
- <?php
- namespace app\admin\command;
- use FilesystemIterator;
- use think\Exception;
- use think\facade\Db;
- use think\facade\Lang;
- use think\console\Input;
- use think\console\Output;
- use think\console\Command;
- use think\facade\Config;
- use app\common\library\Menu;
- use app\admin\model\MenuRule;
- use think\console\input\Option;
- use think\exception\ErrorException;
- use app\admin\command\Crud\library\Stub;
- use UnexpectedValueException;
- class Crud extends Command
- {
- protected $input;
- protected $output;
- /**
- * 命令选项列表
- * solveArray 是否切割数组参数,可自动以`逗号`切割输入值
- * setInputRule 是否设置输入框识别规则,此类选项较多,方便自动化批量设置
- */
- public $options = [
- 'table' => ['name' => 'table', 'shortcut' => 't', 'mode' => Option::VALUE_REQUIRED, 'description' => 'table name without prefix', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- 'controller' => ['name' => 'controller', 'shortcut' => 'c', 'mode' => Option::VALUE_OPTIONAL, 'description' => 'controller name', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- 'model' => ['name' => 'model', 'shortcut' => 'm', 'mode' => Option::VALUE_OPTIONAL, 'description' => 'model name', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- 'fields' => ['name' => 'fields', 'shortcut' => 'f', 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'visible fields', 'default' => null, 'solveArray' => true, 'setInputRule' => false],
- 'force' => ['name' => 'force', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL, 'description' => 'force override or force delete,without tips', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- 'commonmodel' => ['name' => 'commonmodel', 'shortcut' => 'o', 'mode' => Option::VALUE_OPTIONAL, 'description' => 'common model', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- // 关联表
- 'relation' => ['name' => 'relation', 'shortcut' => 'r', 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'relation table name without prefix', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- 'relationmodel' => ['name' => 'relationmodel', 'shortcut' => 'e', 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'relation model name', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- 'relationforeignkey' => ['name' => 'relationforeignkey', 'shortcut' => 'k', 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'relation foreign key', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- 'relationprimarykey' => ['name' => 'relationprimarykey', 'shortcut' => 'p', 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'relation primary key', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- 'relationfields' => ['name' => 'relationfields', 'shortcut' => 'l', 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'relation table fields', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- 'relationmode' => ['name' => 'relationmode', 'shortcut' => 'a', 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'relation table mode,hasone or belongsto', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- // 关联远程下拉select显示字段
- 'remoteselectfield' => ['name' => 'remoteselectfield', 'shortcut' => 's', 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'relation table Remote select table field', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- // 是否删除模式
- 'delete' => ['name' => 'delete', 'shortcut' => 'd', 'mode' => Option::VALUE_OPTIONAL, 'description' => 'delete all files generated by CRUD', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- // 指定数据库
- 'db' => ['name' => 'db', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL, 'description' => 'database config name', 'default' => 'mysql', 'solveArray' => false, 'setInputRule' => false],
- // 快速搜索字段设置
- 'quicksearchfield' => ['name' => 'quicksearchfield', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'quick search field', 'default' => null, 'solveArray' => true, 'setInputRule' => false],
- // 排序字段设置
- 'sortfield' => ['name' => 'sortfield', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL, 'description' => 'sort field', 'default' => null, 'solveArray' => false, 'setInputRule' => false],
- // 排除字段设置
- 'ignorefields' => ['name' => 'ignorefields', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'ignore fields', 'default' => null, 'solveArray' => true, 'setInputRule' => false],
- // 设置后缀识别规则
- 'radiofieldsuffix' => ['name' => 'radiofieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for radio components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'checkboxfieldsuffix' => ['name' => 'checkboxfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for checkbox components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'datetimefieldsuffix' => ['name' => 'datetimefieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for datetime components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'switchfieldsuffix' => ['name' => 'switchfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for switch components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'editorfieldsuffix' => ['name' => 'editorfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for editor components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'textareafieldsuffix' => ['name' => 'textareafieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for textarea components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'cityfieldsuffix' => ['name' => 'cityfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for city components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'remoteselectfieldsuffix' => ['name' => 'remoteselectfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for remote select components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'arrayfieldsuffix' => ['name' => 'arrayfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for array components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'imagefieldsuffix' => ['name' => 'imagefieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for image components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'imagesfieldsuffix' => ['name' => 'imagesfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for images components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'filefieldsuffix' => ['name' => 'filefieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for file components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'filesfieldsuffix' => ['name' => 'filesfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for files components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'numberfieldsuffix' => ['name' => 'numberfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for number components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'selectfieldsuffix' => ['name' => 'selectfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for select components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'selectsfieldsuffix' => ['name' => 'selectsfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for selects components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- 'iconfieldsuffix' => ['name' => 'iconfieldsuffix', 'shortcut' => null, 'mode' => Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'description' => 'Automatically generate field suffixes for icon components', 'default' => null, 'solveArray' => true, 'setInputRule' => true],
- ];
- /**
- * 输入框类型的识别规则
- */
- protected $inputTypeRule = [
- // 开关组件
- [
- 'type' => ['tinyint', 'int', 'enum'],
- 'suffix' => ['switch', 'toggle'],
- 'value' => 'switch',
- ],
- [
- 'column_type' => ['tinyint(1)', 'char(1)', 'tinyint(1) unsigned'],
- 'suffix' => ['switch', 'toggle'],
- 'value' => 'switch',
- ],
- // 富文本-识别规则和textarea重合,优先识别为富文本
- [
- 'type' => ['longtext', 'text', 'mediumtext', 'smalltext', 'tinytext', 'bigtext'],
- 'suffix' => ['content', 'editor'],
- 'value' => 'editor',
- ],
- // textarea
- [
- 'type' => ['varchar'],
- 'suffix' => ['textarea', 'multiline', 'rows'],
- 'value' => 'textarea',
- ],
- // Array
- [
- 'suffix' => ['array'],
- 'value' => 'array',
- ],
- // 时间选择器-字段类型为int同时以['time', 'datetime']结尾
- [
- 'type' => ['int'],
- 'suffix' => ['time', 'datetime'],
- 'value' => 'datetime',
- ],
- [
- 'type' => ['datetime', 'timestamp'],
- 'value' => 'datetime',
- ],
- [
- 'type' => ['date'],
- 'value' => 'date',
- ],
- [
- 'type' => ['year'],
- 'value' => 'year',
- ],
- [
- 'type' => ['time'],
- 'value' => 'time',
- ],
- // 单选select
- [
- 'suffix' => ['select', 'list', 'data'],
- 'value' => 'select',
- ],
- // 多选select
- [
- 'suffix' => ['selects', 'multi', 'lists'],
- 'value' => 'selects',
- ],
- // 远程select
- [
- 'suffix' => ['_id'],
- 'value' => 'remoteSelect',
- ],
- // 远程selects
- [
- 'suffix' => ['_ids'],
- 'value' => 'remoteSelects',
- ],
- // 城市选择器
- [
- 'suffix' => ['city'],
- 'value' => 'city',
- ],
- // 单图上传
- [
- 'suffix' => ['image', 'avatar'],
- 'value' => 'image',
- ],
- // 多图上传
- [
- 'suffix' => ['images', 'avatars'],
- 'value' => 'images',
- ],
- // 文件上传
- [
- 'suffix' => ['file'],
- 'value' => 'file',
- ],
- // 多文件上传
- [
- 'suffix' => ['files'],
- 'value' => 'files',
- ],
- // icon选择器
- [
- 'suffix' => ['icon'],
- 'value' => 'icon',
- ],
- // 单选框
- [
- 'column_type' => ['tinyint(1)', 'char(1)', 'tinyint(1) unsigned'],
- 'suffix' => ['status', 'state', 'type'],
- 'value' => 'radio',
- ],
- // 数字输入框
- [
- 'suffix' => ['number', 'int', 'num'],
- 'value' => 'number',
- ],
- [
- 'type' => ['bigint', 'int', 'mediumint', 'smallint', 'tinyint', 'decimal', 'double', 'float'],
- 'value' => 'number',
- ],
- // 富文本-低权重
- [
- 'type' => ['longtext', 'text', 'mediumtext', 'smalltext', 'tinytext', 'bigtext'],
- 'value' => 'textarea',
- ],
- // 单选框-低权重
- [
- 'type' => ['enum'],
- 'value' => 'radio',
- ],
- // 多选框
- [
- 'type' => ['set'],
- 'value' => 'checkbox',
- ],
- ];
- /**
- * 表格字段渲染方案
- */
- protected $fieldRenderRule = [
- // 字段名称为 ['id'] 则字段宽度为70,且开启排序
- [
- 'name' => ['id'],
- 'attr' => [
- 'width' => 70,
- 'sortable' => 'custom',
- 'operator' => 'RANGE',
- ],
- ],
- // 字段名称为 ['weigh'] 则关闭字段筛选
- [
- 'name' => ['weigh'],
- 'attr' => [
- 'sortable' => 'custom',
- 'operator' => 'false',
- ],
- ],
- // 输入框被判定为 ['number'] 则通用搜索中使用范围筛选
- [
- 'type' => ['number'],
- 'attr' => [
- 'operator' => 'RANGE',
- ],
- ],
- // 输入框被判定为['radio', 'select'] 或者字段名后缀为 ['flag'] 则渲染为 tag
- [
- 'type' => ['radio', 'select'],
- 'suffix' => ['flag'],
- 'attr' => [
- 'render' => 'tag',
- ],
- ],
- // 输入框被判定为 ['textarea', 'editor', '...'] 则隐藏字段
- [
- 'type' => ['textarea', 'editor', 'file', 'files', 'array'],
- 'attr' => [
- 'show' => false,
- ],
- ],
- [
- 'type' => ['checkbox', 'selects'],
- 'suffix' => ['flags'],
- 'attr' => [
- 'render' => 'tags',
- ],
- ],
- [
- 'type' => ['datetime'],
- 'attr' => [
- 'render' => 'datetime',
- 'sortable' => 'custom',
- 'operator' => 'RANGE',
- 'width' => 160
- ],
- ],
- [
- 'type' => ['switch'],
- 'attr' => [
- 'render' => 'switch',
- ],
- ],
- [
- 'type' => ['image'],
- 'suffix' => ['avatar'],
- 'attr' => [
- 'render' => 'image',
- ],
- ],
- [
- 'type' => ['images'],
- 'suffix' => ['avatars'],
- 'attr' => [
- 'render' => 'images',
- ],
- ],
- [
- 'type' => ['icon'],
- 'attr' => [
- 'render' => 'icon',
- ],
- ],
- [
- 'suffix' => ['url'],
- 'attr' => [
- 'render' => 'url',
- ],
- ]
- ];
- /**
- * select远程搜索字段关联
- */
- protected $remoteSelectFieldMap = [
- 'nickname' => ['user_id', 'user_ids', 'admin_id', 'admin_ids'],
- ];
- /**
- * 内部保留词
- */
- protected $reservedKeywords = [
- 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor', 'yield'
- ];
- /**
- * 保留字段
- */
- protected $reservedField = [];
- /**
- * 快速搜索字段
- */
- protected $quicksearchfield = ['id'];
- /**
- * 默认排序字段,el-table只支持单字段排序
- */
- protected $defaultSortField = 'weigh,desc';
- /**
- * 排除字段
- */
- protected $ignoreFields = [];
- /**
- * 添加时间字段
- * @var string
- */
- protected $createTimeField = 'createtime';
- /**
- * 更新时间字段
- * @var string
- */
- protected $updateTimeField = 'updatetime';
- /**
- * 子级菜单数组(权限节点)
- * @var string
- */
- protected $menuChildren = [
- ['type' => 'button', 'title' => '查看', 'name' => '/index', 'status' => '1'],
- ['type' => 'button', 'title' => '添加', 'name' => '/add', 'status' => '1'],
- ['type' => 'button', 'title' => '编辑', 'name' => '/edit', 'status' => '1'],
- ['type' => 'button', 'title' => '删除', 'name' => '/del', 'status' => '1'],
- ['type' => 'button', 'title' => '快速排序', 'name' => '/sortable', 'status' => '1'],
- ];
- protected $langPrefix = '';
- protected $stub = null;
- protected function configure()
- {
- $this->setName('crud')->setDescription('Build CRUD code controller and model, and views from table');
- foreach ($this->options as $option) {
- $this->addOption($option['name'], $option['shortcut'], $option['mode'], $option['description'], $option['default']);
- }
- }
- protected function execute(Input $input, Output $output)
- {
- $this->stub = Stub::instance();
- $this->input = $input;
- $this->output = $output;
- $adminPath = dirname(__DIR__) . DIRECTORY_SEPARATOR;
- $webPath = root_path() . 'web' . DIRECTORY_SEPARATOR;
- $webViewsPath = $webPath . 'src' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'backend' . DIRECTORY_SEPARATOR;
- $webLangPath = $webPath . 'src' . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . 'pages' . DIRECTORY_SEPARATOR;
- $webControllerUrls = $webPath . 'src' . DIRECTORY_SEPARATOR . 'api' . DIRECTORY_SEPARATOR . 'controllerUrls.ts';
- // 数据库配置名
- $db = $this->getOption('db');
- // 表名
- $table = $this->getOption('table');
- if (!$table) {
- throw new Exception('table name can\'t empty');
- }
- // 自定义控制器
- $controller = $this->getOption('controller');
- // 自定义模型
- $model = $this->getOption('model');
- $model = $model ?: $controller;
- // 验证器类
- $validate = $model;
- // 自定义显示字段
- $fields = $this->getOption('fields');
- // 强制覆盖
- $force = $this->getOption('force');
- // 是否将model放在app/common/model中
- $commonModel = $this->getOption('commonmodel');
- // 关联表
- $relation = $this->getOption('relation');
- // 自定义关联表模型
- $relationModel = $this->getOption('relationmodel');
- // 关联模式
- $relationMode = $mode = $this->getOption('relationmode');
- // 外键
- $relationForeignKey = $this->getOption('relationforeignkey');
- // 主键
- $relationPrimaryKey = $this->getOption('relationprimarykey');
- // 远程下拉字段
- $remoteSelectField = $this->getOption('remoteselectfield');
- // 关联表显示字段
- $relationFields = $this->getOption('relationfields');
- // 快速搜索字段
- $quicksearchfield = $this->getOption('quicksearchfield');
- // 排序字段
- $sortfield = $this->getOption('sortfield');
- // 排除字段
- $ignoreFields = $this->getOption('ignorefields');
- // 字段识别后缀规则批量设置
- foreach ($this->options as $option) {
- if ($option['setInputRule']) {
- $field = explode('field', $option['name']);
- $this->setInputTypeRule($field[0], $this->getOption($option['name']));
- }
- }
- if ($quicksearchfield) $this->quicksearchfield = $quicksearchfield;
- if ($ignoreFields) $this->ignoreFields = $ignoreFields;
- if ($sortfield) $this->defaultSortField = $sortfield;
- $this->reservedField = array_merge($this->reservedField, [$this->createTimeField, $this->updateTimeField]);
- $dbconnect = Db::connect($db);
- $prefix = Config::get('database.connections.' . $db . '.prefix');
- $dbname = Config::get('database.connections.' . $db . '.database');
- // 模块
- $moduleName = 'admin';
- $modelModuleName = $validateModuleName = $commonModel ? 'common' : $moduleName;
- // 检查主表
- $modelName = $table = stripos($table, $prefix) === 0 ? substr($table, strlen($prefix)) : $table;
- $modelTableType = 'table';
- $modelTableTypeName = $modelTableName = $modelName;
- $modelTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], true);
- if (!$modelTableInfo) {
- $modelTableType = 'name';
- $modelTableName = $prefix . $modelName;
- $modelTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], true);
- if (!$modelTableInfo) {
- throw new Exception('table not found');
- }
- }
- $modelTableInfo = $modelTableInfo[0];
- $relations = [];
- // 检查关联表
- if ($relation) {
- foreach ($relation as $index => $relationTable) {
- $relationName = stripos($relationTable, $prefix) === 0 ? substr($relationTable, strlen($prefix)) : $relationTable;
- $relationTableType = 'table';
- $relationTableTypeName = $relationTableName = $relationName;
- $relationTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], true);
- if (!$relationTableInfo) {
- $relationTableType = 'name';
- $relationTableName = $prefix . $relationName;
- $relationTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], true);
- if (!$relationTableInfo) {
- throw new Exception('relation table not found');
- }
- }
- $relationTableInfo = $relationTableInfo[0];// 关联表信息
- $relationModelTemp = $relationModel[$index] ?? '';// 关联模型
- [$relationNamespace, $relationName, $relationFile] = $this->getModelData($modelModuleName, $relationModelTemp, $relationName);
- $relations[] = [
- // 关联表基础名
- 'relationName' => $relationName,
- // 关联表类命名空间
- 'relationNamespace' => $relationNamespace,
- // 关联模型名
- 'relationModel' => $relationModelTemp,
- // 关联文件
- 'relationFile' => $relationFile,
- // 关联表名称
- 'relationTableName' => $relationTableName,
- // 关联表信息
- 'relationTableInfo' => $relationTableInfo,
- // 关联模型表类型(name或table)
- 'relationTableType' => $relationTableType,
- // 关联模型表类型名称
- 'relationTableTypeName' => $relationTableTypeName,
- // 关联表字段
- 'relationFields' => isset($relationFields[$index]) ? explode(',', $relationFields[$index]) : [],
- // 关联模式
- 'relationMode' => $relationMode[$index] ?? 'belongsto',
- // 关联表外键
- 'relationForeignKey' => $relationForeignKey[$index] ?? parse_name($relationName) . '_id',
- // 关联表主键
- 'relationPrimaryKey' => $relationPrimaryKey[$index] ?? 'id',
- // 远程下拉字段
- 'remoteSelectField' => $remoteSelectField[$index] ?? '',
- ];
- }
- }
- // 控制器
- [$controllerNamespace, $controllerName, $controllerFile, $controllerArr] = $this->getControllerData($moduleName, $controller, $table);
- // 模型
- [$modelNamespace, $modelName, $modelFile, $modelArr] = $this->getModelData($modelModuleName, $model, $table);
- // 验证器
- [$validateNamespace, $validateName, $validateFile, $validateArr] = $this->getValidateData($validateModuleName, $validate, $table);
- // 处理基础文件名,取消所有下划线并转换为小写
- $baseNameArr = $controllerArr;
- $baseFileName = parse_name(array_pop($baseNameArr));
- array_push($baseNameArr, $baseFileName);
- $controllerBaseName = strtolower(implode(DIRECTORY_SEPARATOR, $baseNameArr));
- // 视图文件
- $viewArr = $controllerArr;
- foreach ($viewArr as $index => $item) {
- $viewArr[$index] = parse_name($item, 1, false);
- }
- $viewPath = implode(DIRECTORY_SEPARATOR, $viewArr);
- $viewDir = $webViewsPath . $viewPath . DIRECTORY_SEPARATOR;
- $this->langPrefix = implode('.', $viewArr) . '.';
- $controllerUrl = $originControllerUrl = implode('/', $viewArr);
- $controllerUrl = '/admin/' . preg_replace("/\//", '.', $controllerUrl) . '/';
- $controllerUrlVarName = '';
- foreach ($viewArr as $item) {
- $controllerUrlVarName .= parse_name($item, 1);
- }
- $controllerUrlVarName = lcfirst($controllerUrlVarName);
- // 最终将生成的文件路径
- $formFile = $viewDir . 'popupForm.vue';
- $indexFile = $viewDir . 'index.vue';
- // $serverLangZhCnFile = $adminPath . 'lang' . DIRECTORY_SEPARATOR . 'zh-cn' . DIRECTORY_SEPARATOR . $controllerBaseName . '.php';
- // $serverLangEnFile = $adminPath . 'lang' . DIRECTORY_SEPARATOR . 'en' . DIRECTORY_SEPARATOR . $controllerBaseName . '.php';
- $webLangZhCnFile = $webLangPath . 'zh-cn' . DIRECTORY_SEPARATOR . $viewPath . '.ts';
- $webLangEnFile = $webLangPath . 'en' . DIRECTORY_SEPARATOR . $viewPath . '.ts';
- // 是否为删除模式
- $delete = $this->getOption('delete');
- if ($delete) {
- $readyFiles = [$controllerFile, $modelFile, $validateFile, $formFile, $indexFile, $webLangZhCnFile, $webLangEnFile];
- foreach ($readyFiles as $v) {
- $output->warning($v);
- }
- if (!$force) {
- $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: ");
- $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r'));
- if (trim($line) != 'yes') {
- throw new Exception('Operation is aborted!');
- }
- }
- foreach ($readyFiles as $v) {
- if (file_exists($v)) {
- unlink($v);
- }
- //删除空文件夹
- switch ($v) {
- case $modelFile:
- $this->removeEmptyBaseDir($v, $modelArr);
- break;
- case $validateFile:
- $this->removeEmptyBaseDir($v, $validateArr);
- break;
- case $formFile:
- case $indexFile:
- $this->removeEmptyBaseDir($v, $viewArr, 0);
- break;
- default:
- $this->removeEmptyBaseDir($v, $controllerArr);
- }
- }
- // 删除菜单
- Menu::delete($originControllerUrl, true);
- $output->info('Delete Successed');
- return;
- }
- // 非覆盖模式时如果存在控制器文件则报错
- if (is_file($controllerFile) && !$force) {
- throw new Exception("controller already exists!\nIf you need to rebuild again, use the parameter --force=true ");
- }
- // 非覆盖模式时如果存在模型文件则报错
- if (is_file($modelFile) && !$force) {
- throw new Exception("model already exists!\nIf you need to rebuild again, use the parameter --force=true ");
- }
- // 非覆盖模式时如果存在验证文件则报错
- if (is_file($validateFile) && !$force) {
- throw new Exception("validate already exists!\nIf you need to rebuild again, use the parameter --force=true ");
- }
- require $adminPath . 'common.php';
- // 从数据库中获取表字段信息
- $sql = 'SELECT * FROM `information_schema`.`columns` '
- . 'WHERE TABLE_SCHEMA = ? AND table_name = ? '
- . 'ORDER BY ORDINAL_POSITION';
- // 加载主表的列
- $columnList = $dbconnect->query($sql, [$dbname, $modelTableName]);
- $fieldArr = [];
- foreach ($columnList as $v) {
- $fieldArr[] = $v['COLUMN_NAME'];
- }
- // 加载关联表的列
- foreach ($relations as &$relation) {
- $relationColumnList = $dbconnect->query($sql, [$dbname, $relation['relationTableName']]);
- $relationFieldList = [];
- foreach ($relationColumnList as $v) {
- $relationFieldList[] = $v['COLUMN_NAME'];
- }
- if (!$relation['relationPrimaryKey']) {
- foreach ($relationColumnList as $v) {
- if ($v['COLUMN_KEY'] == 'PRI') {
- $relation['relationPrimaryKey'] = $v['COLUMN_NAME'];
- break;
- }
- }
- }
- // 如果主键为空
- if (!$relation['relationPrimaryKey']) {
- throw new Exception('Relation Primary key not found!');
- }
- // 如果主键不在表字段中
- if (!in_array($relation['relationPrimaryKey'], $relationFieldList)) {
- throw new Exception('Relation Primary key not found in table!');
- }
- $relation['relationColumnList'] = $relationColumnList;
- $relation['relationFieldList'] = $relationFieldList;
- }
- unset($relation);
- // 检查主键是否存在
- $priKey = '';// 主键
- foreach ($columnList as $v) {
- if ($v['COLUMN_KEY'] == 'PRI') {
- $priKey = $v['COLUMN_NAME'];
- break;
- }
- }
- if (!$priKey) {
- throw new Exception('Primary key not found!');
- }
- // 检查快速搜索字段是否存在
- foreach ($this->quicksearchfield as $key => $item) {
- if (!in_array($item, $fieldArr)) {
- unset($this->quicksearchfield[$key]);
- }
- }
- if (!$this->quicksearchfield) {
- $this->quicksearchfield = [$priKey];
- }
- // 如果是关联模型
- foreach ($relations as &$relation) {
- if ($relation['relationMode'] == 'hasone') {
- $relationForeignKey = $relation['relationForeignKey'] ?: $table . '_id';
- $relationPrimaryKey = $relation['relationPrimaryKey'] ?: $priKey;
- if (!in_array($relationForeignKey, $relation['relationFieldList'])) {
- throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationForeignKey . ']');
- }
- if (!in_array($relationPrimaryKey, $fieldArr)) {
- throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationPrimaryKey . ']');
- }
- } else {
- $relationForeignKey = $relation['relationForeignKey'] ?: parse_name($relation['relationName']) . '_id';
- $relationPrimaryKey = $relation['relationPrimaryKey'] ?: $relation['relationPriKey'];
- if (!in_array($relationForeignKey, $fieldArr)) {
- throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationForeignKey . ']');
- }
- if (!in_array($relationPrimaryKey, $relation['relationFieldList'])) {
- throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationPrimaryKey . ']');
- }
- }
- $relation['relationForeignKey'] = $relationForeignKey;
- $relation['relationPrimaryKey'] = $relationPrimaryKey;
- $relation['relationClassName'] = $modelNamespace != $relation['relationNamespace'] ? '\\' . $relation['relationNamespace'] . '\\' . $relation['relationName'] . '::class' : $relation['relationName'] . '::class';
- }
- unset($relation);
- try {
- $quickSearchFieldNames = [];// 快速搜索字段列表
- $formFieldList = [];// 表单字段列表
- $dblClickNotEditColumn = ['undefined'];// 不允许双击编辑的字段
- $optButtons = ['edit', 'delete'];// 表格行操作按钮
- $inputDefaultItems = [];// 表单默认值
- $defaultOrder = [];// 默认排序字段
- $langList = ['en' => [], 'zh-cn' => []];// web端语言项列表
- $controllerAttrList = [];// 控制器内要设置的变量列表
- $importControllerUrls = [];// 要引入的控制器url列表
- $importPackages = '';
- $formItemRules = [];// 字段验证规则
- $formDialogBig = false;// 存在富文本编辑器则加宽表单dialog
- $modelSetAttrArr = [];
- $modelFieldType = [];
- $enableDragSort = in_array('weigh', $fieldArr);
- $controllerData = [
- 'controllerNamespace' => $controllerNamespace,
- 'controllerName' => $controllerName,
- 'modelName' => $modelName,
- 'modelNamespace' => $modelNamespace,
- 'methods' => [],
- ];
- // 表格列数据
- $tableColumnList = [
- [
- 'type' => 'selection',
- 'align' => 'center',
- 'operator' => 'false',
- ],
- ];
- foreach ($columnList as $column) {
- $field = $column['COLUMN_NAME'];
- $inputType = $this->getFieldInputType($column);
- // 列替换数据
- $columnData = $this->getColumnReplaceData($column, $field, $inputType);
- // 字段语言包数据
- if ($column['COLUMN_COMMENT'] != '') {
- $columnLang = $this->getLangItem($field, $column['COLUMN_COMMENT']);
- $langList['en'] = array_merge($langList['en'], $columnLang['en']);
- $langList['zh-cn'] = array_merge($langList['zh-cn'], $columnLang['zh-cn']);
- }
- // 快速搜索字段
- if (in_array($field, $this->quicksearchfield)) {
- $quickSearchFieldNames[] = $column['COLUMN_COMMENT'] ?? $field;
- }
- // 不允许双击编辑的字段
- if ($inputType == 'switch') {
- $dblClickNotEditColumn[] = $field;
- }
- // 表单字段
- if ($column['COLUMN_KEY'] != 'PRI' && !in_array($field, $this->reservedField) && !in_array($field, $this->ignoreFields)) {
- // 输入框默认值
- if ($column['COLUMN_DEFAULT'] || ($column['COLUMN_DEFAULT'] === '0' && $inputType != 'remoteSelect' && $inputType != 'remoteSelects')) {
- if (in_array($inputType, ['checkbox', 'selects', 'remoteSelects', 'city'])) {
- $column['COLUMN_DEFAULT'] = explode(',', $column['COLUMN_DEFAULT']);
- }
- $inputDefaultItems[$field] = $column['COLUMN_DEFAULT'];
- }
- $formFieldList[$field] = [
- ':label' => 't(\'' . $this->langPrefix . $field . '\')',
- 'type' => $inputType,
- 'v-model' => 'baTable.form.items!.' . $field,
- 'prop' => $field,
- ];
- // 输入框类型的特殊属性
- if (in_array($inputType, ['radio', 'checkbox', 'select', 'selects'])) {
- $formFieldList[$field][':data'] = [
- 'content' => $columnData,
- ];
- } elseif ($inputType == 'textarea') {
- $formFieldList[$field][':input-attr']['rows'] = 3;
- $formFieldList[$field]['@keyup.enter.stop'] = '';
- $formFieldList[$field]['@keyup.ctrl.enter'] = 'baTable.onSubmit(formRef)';
- } elseif ($inputType == 'remoteSelect' || $inputType == 'remoteSelects') {
- $formFieldList[$field][':input-attr']['field'] = $this->getRemoteSelectField($field, $relations);
- $remoteUrl = $this->getRemoteSelectUrl($field, $relations, $webControllerUrls);
- $formFieldList[$field][':input-attr']['remote-url'] = $remoteUrl['url'];
- $formFieldList[$field][':input-attr']['pk'] = $remoteUrl['pk'];
- if ($remoteUrl['importControllerUrls']) {
- $importControllerUrls[$remoteUrl['importControllerUrls']] = '';
- }
- if ($inputType == 'remoteSelects') {
- $formFieldList[$field]['type'] = 'remoteSelect';
- $formFieldList[$field][':input-attr']['multiple'] = 'true';
- }
- } elseif ($inputType == 'number') {
- $formFieldList[$field]['v-model.number'] = $formFieldList[$field]['v-model'];
- unset($formFieldList[$field]['v-model']);
- $formFieldList[$field][':input-attr']['step'] = $column['NUMERIC_SCALE'] > 0 ? '0.' . str_repeat(0, $column['NUMERIC_SCALE'] - 1) . '1' : 1;
- } elseif ($inputType == 'icon') {
- $formFieldList[$field][':input-attr']['placement'] = 'top';
- } elseif ($inputType == 'array') {
- $modelFieldType[$field] = 'json';
- $inputDefaultItems[$field] = [];
- } elseif ($inputType == 'datetime' && $column['DATA_TYPE'] == 'int') {
- $modelFieldType[$field] = 'timestamp:Y-m-d H:i:s';
- } elseif ($inputType == 'editor') {
- $formFieldList[$field]['@keyup.enter.stop'] = '';
- $formFieldList[$field]['@keyup.ctrl.enter'] = 'baTable.onSubmit(formRef)';
- $formDialogBig = true; // form 使用较宽的 Dialog
- // 重写edit和add方法
- $controllerData['methods']['add'] = $this->stub->getReplacedStub('mixins' . DIRECTORY_SEPARATOR . 'controllerEditorAddMethod', []);
- $controllerData['methods']['edit'] = $this->stub->getReplacedStub('mixins' . DIRECTORY_SEPARATOR . 'controllerEditorEditMethod', []);
- }
- // 模型的属性修改器获取器
- $this->getModelAttrMethod($modelSetAttrArr, $field, $inputType, $column);
- // placeholder
- if (!in_array($inputType, ['image', 'images', 'file', 'files'])) {
- if (in_array($inputType, ['radio', 'checkbox', 'datetime', 'year', 'date', 'time', 'select', 'selects', 'remoteSelect', 'remoteSelects', 'city', 'icon'])) {
- $formFieldList[$field][':input-attr']['placeholder'] = "t('Please select field', { field: t('" . $this->langPrefix . $field . "') })";
- } else {
- $formFieldList[$field][':input-attr']['placeholder'] = "t('Please input field', { field: t('" . $this->langPrefix . $field . "') })";
- }
- }
- // 字段验证规则
- if ($column['IS_NULLABLE'] == 'NO' && !in_array($inputType, ['switch', 'icon'])) {
- if ($inputType == 'editor') {
- $formItemRules[$field][] = "buildValidatorData({name: 'editorRequired', message: t('Please input field', { field: t('" . $this->langPrefix . $field . "') })})";
- } elseif (in_array($inputType, ['radio', 'checkbox', 'datetime', 'year', 'date', 'time', 'select', 'selects', 'remoteSelect', 'city', 'image', 'images', 'file', 'files', 'icon'])) {
- $formItemRules[$field][] = "buildValidatorData({name: 'required', message: t('Please select field', { field: t('" . $this->langPrefix . $field . "') })})";
- } else {
- $formItemRules[$field][] = "buildValidatorData({name: 'required', title: t('" . $this->langPrefix . $field . "')})";
- }
- }
- if ($field == 'mobile') {
- $formItemRules[$field][] = "buildValidatorData({name: 'mobile', message: t('Please enter the correct field', { field: t('" . $this->langPrefix . $field . "') })})";
- }
- if ($inputType == 'datetime' || $inputType == 'date') {
- $formItemRules[$field][] = "buildValidatorData({name: 'date', message: t('Please enter the correct field', { field: t('" . $this->langPrefix . $field . "') })})";
- }
- }
- // 表格列
- if (!$fields || in_array($field, $fields)) {
- $tableColumnList[] = $this->getTableColumn($field, $inputType, $column['DATA_TYPE'], $columnData);
- }
- }
- // 关联表
- foreach ($relations as $relation) {
- foreach ($relation['relationColumnList'] as $v) {
- // 不显示的字段直接过滤掉
- if ($relation['relationFields'] && !in_array($v['COLUMN_NAME'], $relation['relationFields'])) {
- continue;
- }
- $inputType = $this->getFieldInputType($v);
- $relationField = strtolower($relation['relationName']) . '.' . $v['COLUMN_NAME'];
- $relationFieldLang = strtolower($relation['relationName']) . '__' . $v['COLUMN_NAME'];
- // 列替换数据
- $columnData = $this->getColumnReplaceData($v, $relationFieldLang, $inputType);
- // 不允许双击编辑的字段
- if ($inputType == 'switch') {
- $dblClickNotEditColumn[] = $relationField;
- }
- // 表格列
- $tableColumnList[] = $this->getTableColumn($relationField, $inputType, $v['DATA_TYPE'], $columnData, $relationFieldLang);
- // 字段语言包数据
- if ($v['COLUMN_COMMENT'] != '') {
- $columnLang = $this->getLangItem($relationFieldLang, $v['COLUMN_COMMENT']);
- $langList['en'] = array_merge($langList['en'], $columnLang['en']);
- $langList['zh-cn'] = array_merge($langList['zh-cn'], $columnLang['zh-cn']);
- }
- }
- }
- Stub::buildFormField($formFieldList);
- Stub::buildFormRules($formItemRules);
- // 表格列额外处理
- $tableColumnList[] = [
- 'label' => "t('operate')",
- 'align' => 'center',
- 'width' => $enableDragSort ? 140 : 100,
- 'render' => 'buttons',
- 'buttons' => 'optButtons',
- 'operator' => 'false',
- ];
- foreach ($tableColumnList as $key => $item) {
- if (isset($item['show']) && !$item['show']) {
- unset($tableColumnList[$key]);
- }
- }
- Stub::buildTableColumn($tableColumnList);
- // 不允许双击编辑的字段
- Stub::buildDblClickNotEditColumn($dblClickNotEditColumn);
- // 开启拖拽排序
- if ($enableDragSort) {
- array_unshift($optButtons, 'weigh-sort');
- }
- $optButtons = json_encode($optButtons);
- // 处理快速搜索提示信息
- $langList['en']['quick Search Fields'] = implode('、', $this->quicksearchfield);
- $langList['zh-cn']['quick Search Fields'] = implode('、', $quickSearchFieldNames);
- $controllerAttrList['quickSearchField'] = $this->quicksearchfield;
- // 输入框默认值
- $inputDefaultItems = json_encode($inputDefaultItems);
- // 默认排序
- if ($this->defaultSortField) {
- $sortItem = explode(',', $this->defaultSortField);
- if (isset($sortItem[0]) && isset($sortItem[1]) && in_array($sortItem[0], $fieldArr)) {
- $defaultOrder = $sortItem;
- }
- }
- if (!$defaultOrder) {
- $defaultOrder = [$priKey, 'desc'];
- }
- $defaultOrderStub = Stub::buildDefaultOrder($defaultOrder);
- $controllerAttrList['defaultSortField'] = implode(',', $defaultOrder);
- // 添加Url到controllerUrls.ts
- $appendUrl = "export const {$controllerUrlVarName} = '{$controllerUrl}'";
- $webControllerUrlsContents = @file_get_contents($webControllerUrls);
- if (strpos($webControllerUrlsContents, $appendUrl) === false) {
- @file_put_contents($webControllerUrls, $appendUrl . "\n", FILE_APPEND);
- }
- // 组装index.vue
- $indexVue = $this->stub->getReplacedStub('html' . DIRECTORY_SEPARATOR . 'index', [
- 'tablePk' => $priKey,
- 'controllerUrlVarName' => $controllerUrlVarName,
- 'originControllerUrl' => $originControllerUrl,
- 'tableColumnList' => $tableColumnList,
- 'dblClickNotEditColumn' => $dblClickNotEditColumn,
- 'optButtons' => $optButtons,
- 'inputDefaultItems' => $inputDefaultItems ?: '',
- 'defaultOrderStub' => $defaultOrderStub,
- 'langPrefix' => $this->langPrefix,
- ]);
- Stub::writeToFile($indexFile, $indexVue);
- // 组装form.vue
- if ($importControllerUrls) {
- $importPackages .= "import { " . implode(',', array_keys($importControllerUrls)) . " } from '/@/api/controllerUrls'\n";
- }
- $formDialogBig = $formDialogBig ? "\n" . Stub::tab(2) . "width='50%'" : '';
- $formVue = $this->stub->getReplacedStub('html' . DIRECTORY_SEPARATOR . 'form', [
- 'formItem' => $formFieldList ?: '',
- 'importPackages' => $importPackages,
- 'formItemRules' => $formItemRules ?: '',
- 'formDialogBig' => $formDialogBig,
- ]);
- Stub::writeToFile($formFile, $formVue);
- // 组装和写入web语言包
- Stub::writeWebLangFile($langList, $webLangEnFile, $webLangZhCnFile);
- // 表注释
- $tableComment = mb_substr($modelTableInfo['Comment'], -1) == '表' ? mb_substr($modelTableInfo['Comment'], 0, -1) . '管理' : $modelTableInfo['Comment'];
- $modelData = [
- 'modelNamespace' => $modelNamespace,
- 'modelName' => $modelName,
- 'controllerUrlVarName' => $controllerUrlVarName,
- 'modelConnection' => $db == 'mysql' ? '' : "\n" . Stub::tab() . "protected \$connection = '{$db}';",
- 'modelTableType' => $modelTableType,
- 'modelTableTypeName' => $modelTableTypeName,
- 'modelAutoWriteTimestamp' => in_array($this->createTimeField, $fieldArr) || in_array($this->updateTimeField, $fieldArr) ? "'int'" : 'false',
- 'createTime' => in_array($this->createTimeField, $fieldArr) ? "'{$this->createTimeField}'" : 'false',
- 'updateTime' => in_array($this->updateTimeField, $fieldArr) ? "'{$this->updateTimeField}'" : 'false',
- 'modeAfterInsert' => '',
- 'priKey' => $priKey == 'id' ? '' : "\n" . Stub::tab() . "// 表主键\n" . Stub::tab() . 'protected $pk = ' . "'{$priKey}';\n" . Stub::tab(),
- ];
- $controllerData['tableComment'] = $tableComment;
- if ($priKey != $defaultOrder[0]) {
- $modelData['modeAfterInsert'] = $this->stub->getReplacedStub('mixins' . DIRECTORY_SEPARATOR . 'modeAfterInsert', [
- 'order' => $defaultOrder[0]
- ]);
- }
- // 如果使用关联模型
- if ($relations) {
- $relationWithList = $relationMethodList = $relationVisibleFieldList = [];
- foreach ($relations as $relation) {
- // 需要构造关联的方法
- $relation['relationMethod'] = strtolower($relation['relationName']);
- // 关联的模式
- $relation['relationMode'] = $relation['relationMode'] == 'hasone' ? 'hasOne' : 'belongsTo';
- // 关联字段
- $relation['relationPrimaryKey'] = $relation['relationPrimaryKey'] ?: $priKey;
- // 预载入的方法
- $relationWithList[] = $relation['relationMethod'];
- unset($relation['relationColumnList'], $relation['relationFieldList'], $relation['relationTableInfo']);
- // 构造关联模型的方法
- $relationMethodList[] = $this->stub->getReplacedStub('mixins' . DIRECTORY_SEPARATOR . 'modelBelongsToMethod', $relation);
- // 显示的字段
- if ($relation['relationFields']) {
- $relationVisibleFieldList[] = "\$res->visible(['{$relation['relationMethod']}' => ['" . implode("','", $relation['relationFields']) . "']]);";
- }
- }
- $controllerData['relationVisibleFieldList'] = $relationVisibleFieldList ? implode("\n" . Stub::tab(2), $relationVisibleFieldList) : '';
- $controllerAttrList['withJoinTable'] = $relationWithList;
- // 需要重写index方法
- if ($controllerData['relationVisibleFieldList']) {
- $controllerData['methods']['index'] = $this->stub->getReplacedStub('mixins' . DIRECTORY_SEPARATOR . 'controllerIndex', $controllerData);
- }
- }
- // 排除字段
- $preExcludeFields = ['createtime', 'updatetime', 'salt'];
- $controllerExcludeFields = [];
- foreach ($preExcludeFields as $preExcludeField) {
- if (in_array($preExcludeField, $fieldArr)) {
- $controllerExcludeFields[] = $preExcludeField;
- }
- }
- $controllerAttrList['preExcludeFields'] = $controllerExcludeFields;
- // 控制器属性
- $controllerAttr = '';
- foreach ($controllerAttrList as $key => $item) {
- if (is_array($item)) {
- $controllerAttr .= "\n" . Stub::tab() . "protected \${$key} = ['" . implode("', '", $item) . "'];\n";
- } else {
- $controllerAttr .= "\n" . Stub::tab() . "protected \${$key} = '$item';\n";
- }
- }
- $controllerData['controllerAttr'] = $controllerAttr;
- $controllerData['methods'] = (isset($controllerData['methods']) && $controllerData['methods']) ? implode("\n", $controllerData['methods']) : '';
- $controllerContent = $this->stub->getReplacedStub('controller', $controllerData);
- // 生成控制器文件
- Stub::writeToFile($controllerFile, $controllerContent);
- // 生成模型文件
- $modelMethodList = array_merge($modelSetAttrArr, $relationMethodList ?? []);
- $modelData['modelMethodList'] = $modelMethodList ? implode("\n", $modelMethodList) : '';
- $modelData['modelFieldType'] = Stub::buildModelFieldType($modelFieldType);
- $modelContent = $this->stub->getReplacedStub('model', $modelData);
- Stub::writeToFile($modelFile, $modelContent);
- // 生成关联模型文件
- if ($relations) {
- foreach ($relations as $relation) {
- if (!is_file($relation['relationFile'])) {
- $relationFileContent = $this->stub->getReplacedStub('relationModel', $relation);
- Stub::writeToFile($relation['relationFile'], $relationFileContent);
- }
- }
- }
- // 生成验证器文件
- Stub::writeToFile($validateFile, $this->stub->getReplacedStub('validate', [
- 'validateNamespace' => $validateNamespace,
- 'validateName' => $validateName
- ]));
- // TODO 生成server端语言包文件备用
- } catch (ErrorException $e) {
- throw new Exception('Code: ' . $e->getCode() . "\nLine: " . $e->getLine() . "\nMessage: " . $e->getMessage() . "\nFile: " . $e->getFile());
- }
- // 继续生成菜单
- if (MenuRule::where('name', $originControllerUrl)->value('id')) {
- $output->warning('Menu rules are not automatically created because they are repeated!');
- } else {
- // 建立菜单目录
- array_pop($baseNameArr);
- $pid = 0;
- if ($baseNameArr) {
- foreach ($baseNameArr as $item) {
- $pMenu = MenuRule::where('name', $item)->value('id');
- if ($pMenu) {
- $pid = $pMenu;
- continue;
- }
- $menu = [
- 'pid' => $pid,
- 'type' => 'menu_dir',
- 'title' => $item,
- 'name' => $item,
- 'path' => $item,
- ];
- $menu = MenuRule::create($menu);
- $pid = $menu->id;
- }
- }
- // 建立菜单
- foreach ($this->menuChildren as &$item) {
- $item['name'] = $originControllerUrl . $item['name'];
- }
- $componentPath = '/' . str_replace($webPath, '', $indexFile);
- $componentPath = str_replace('\\', '/', $componentPath);
- Menu::create([
- [
- 'type' => 'menu',
- 'title' => $tableComment ?: $table,
- 'name' => $originControllerUrl,
- 'path' => $originControllerUrl,
- 'menu_type' => 'tab',
- 'component' => $componentPath,
- 'children' => $this->menuChildren,
- ]
- ], $pid);
- }
- $output->info('Build Successed');
- }
- /**
- * 获取输入选项
- * @param $name string
- * @return string[]|string
- */
- public function getOption(string $name)
- {
- $value = $this->input->getOption($name);
- // 数组处理,兼容[php think crud -f a,b -f c -f d,e,f]
- if (array_key_exists($name, $this->options) && $this->options[$name]['solveArray']) {
- $valueTmp = [];
- foreach ($value as $item) {
- if (stripos($item, ',') !== false) {
- $valueTmp = array_merge($valueTmp, explode(',', $item));
- } elseif (stripos($item, ',') !== false) {
- $valueTmp = array_merge($valueTmp, explode(',', $item));
- } else {
- $valueTmp[] = $item;
- }
- }
- return $valueTmp;
- }
- return $value;
- }
- public function setInputTypeRule($inputType, $newSuffix = null, $newType = null)
- {
- foreach ($this->inputTypeRule as &$item) {
- if ($item['value'] == $inputType) {
- if ($newSuffix) $item['suffix'] = $newSuffix;
- if ($newType) $item['suffix'] = $newType;
- }
- }
- }
- /**
- * 获取模型相关信息.
- *
- * @param $module
- * @param $model
- * @param $table
- * @return array
- */
- protected function getModelData($module, $model, $table): array
- {
- return $this->getParseNameData($module, $model, $table, 'model');
- }
- /**
- * 获取控制器相关信息.
- * @param $module
- * @param $controller
- * @param $table
- * @return array
- */
- protected function getControllerData($module, $controller, $table): array
- {
- return $this->getParseNameData($module, $controller, $table, 'controller');
- }
- /**
- * 获取验证器相关信息.
- * @param $module
- * @param $validate
- * @param $table
- * @return array
- */
- protected function getValidateData($module, $validate, $table): array
- {
- return $this->getParseNameData($module, $validate, $table, 'validate');
- }
- /**
- * 获取已解析相关信息.
- *
- * @param string $module 模块名称
- * @param string $name 自定义名称
- * @param string $table 数据表名
- * @param string $type 解析类型,本例中为controller、model、validate
- * @return array
- */
- protected function getParseNameData($module, $name, $table, $type): array
- {
- $arr = [];
- if (!$name) {
- $parseName = parse_name($table, 1);
- $parseArr = [$table];
- } else {
- $name = str_replace(['.', '/', '\\'], '/', $name);
- $arr = explode('/', $name);
- $parseName = ucfirst(array_pop($arr));
- $parseArr = $arr;
- array_push($parseArr, $parseName);
- }
- //类名不能为内部关键字
- if (in_array(strtolower($parseName), $this->reservedKeywords)) {
- throw new Exception('Unable to use internal variable:' . $parseName);
- }
- $appNamespace = 'app'; //Config::get('app_namespace');
- $parseNamespace = "{$appNamespace}\\{$module}\\{$type}" . ($arr ? '\\' . implode('\\', $arr) : '');
- $moduleDir = app()->getBasePath() . $module . DIRECTORY_SEPARATOR;
- $parseFile = $moduleDir . $type . DIRECTORY_SEPARATOR . ($arr ? implode(DIRECTORY_SEPARATOR, $arr) . DIRECTORY_SEPARATOR : '') . $parseName . '.php';
- return [$parseNamespace, $parseName, $parseFile, $parseArr];
- }
- /**
- * 移除相对的空目录.
- * @param string $parseFile
- * @param array $parseArr
- * @param int $level
- * @return bool
- */
- protected function removeEmptyBaseDir(string $parseFile, array $parseArr, int $level = 1): bool
- {
- if (count($parseArr) > $level) {
- $parentDir = dirname($parseFile);
- for ($i = 0; $i < count($parseArr); $i++) {
- try {
- $iterator = new FilesystemIterator($parentDir);
- $isDirEmpty = !$iterator->valid();
- if ($isDirEmpty) {
- rmdir($parentDir);
- $parentDir = dirname($parentDir);
- } else {
- return true;
- }
- } catch (UnexpectedValueException $e) {
- return false;
- }
- }
- }
- return true;
- }
- protected function getItemArray($item, $field, $comment)
- {
- $itemArr = [];
- $comment = str_replace(',', ',', $comment);
- if (stripos($comment, ':') !== false && stripos($comment, ',') && stripos($comment, '=') !== false) {
- [$fieldLang, $item] = explode(':', $comment);
- foreach (explode(',', $item) as $v) {
- $valArr = explode('=', $v);
- if (count($valArr) == 2) {
- [$key, $value] = $valArr;
- $itemArr[$key] = $field . ' ' . $key;
- }
- }
- } else {
- foreach ($item as $v) {
- $itemArr[$v] = is_numeric($v) ? $field . ' ' . $v : $v;
- }
- }
- return $itemArr;
- }
- public function getTableColumn($field, $inputType, $fieldType, $columnData, $relationFieldLang = null)
- {
- if (!$relationFieldLang) $relationFieldLang = $field;
- $nameBool = false;
- $typeBool = false;
- $suffixBool = false;
- $extendAttr = [];
- foreach ($this->fieldRenderRule as $rule) {
- if (!$rule['attr']) {
- continue;
- }
- if (isset($rule['name']) && $rule['name'] && in_array($field, $rule['name'])) {
- $nameBool = true;
- }
- if (isset($rule['type']) && $rule['type'] && in_array($inputType, $rule['type'])) {
- $typeBool = true;
- }
- if (isset($item['suffix']) && $item['suffix'] && $this->isMatchSuffix($field, $item['suffix'])) {
- $suffixBool = true;
- }
- if ($nameBool || $typeBool || $suffixBool) {
- $extendAttr = $rule['attr'];
- break;
- }
- }
- $column = [
- 'label' => "t('" . $this->langPrefix . $relationFieldLang . "')",
- 'prop' => $field,
- 'align' => 'center',
- ];
- $column = array_merge($column, $extendAttr);
- $columnReplaceValue = [
- 'tag',
- 'tags',
- 'switch',
- ];
- if (isset($column['render']) && in_array($column['render'], $columnReplaceValue)) {
- $column['replaceValue'] = $columnData;
- }
- return $column;
- }
- protected function getLangItem($field, $content)
- {
- $en[$field] = $field;
- $zhCn[$field] = $field;
- if ($content || !Lang::has($field)) {
- $content = str_replace(',', ',', $content);
- $content = str_replace(':', ':', $content);
- if (stripos($content, ':') !== false && stripos($content, ',') && stripos($content, '=') !== false) {
- [$fieldLang, $item] = explode(':', $content);
- $zhCn[$field] = $fieldLang;
- foreach (explode(',', $item) as $v) {
- $valArr = explode('=', $v);
- if (count($valArr) == 2) {
- [$key, $value] = $valArr;
- $en[$field . ' ' . $key] = $field . ' ' . $key;
- $zhCn[$field . ' ' . $key] = $value;
- }
- }
- } else {
- $zhCn[$field] = $content;
- }
- }
- return [
- 'en' => $en,
- 'zh-cn' => $zhCn
- ];
- }
- protected function getFieldInputType($v)
- {
- $inputType = 'string';
- foreach ($this->inputTypeRule as $item) {
- $typeBool = true;
- $suffixBool = true;
- $columnTypeBool = true;
- if (isset($item['type']) && $item['type'] && !in_array($v['DATA_TYPE'], $item['type'])) {
- $typeBool = false;
- }
- if (isset($item['suffix']) && $item['suffix']) {
- $suffixBool = $this->isMatchSuffix($v['COLUMN_NAME'], $item['suffix']);
- }
- if (isset($item['column_type']) && $item['column_type'] && !in_array($v['COLUMN_TYPE'], $item['column_type'])) {
- $columnTypeBool = false;
- }
- if ($typeBool && $suffixBool && $columnTypeBool) {
- return $item['value'];
- }
- }
- return $inputType;
- }
- /**
- * 判断是否符合指定后缀
- *
- * @param string $field 字段名称
- * @param mixed $suffixArr 后缀
- * @return bool
- */
- protected function isMatchSuffix($field, $suffixArr)
- {
- $suffixArr = is_array($suffixArr) ? $suffixArr : explode(',', $suffixArr);
- foreach ($suffixArr as $v) {
- if (preg_match("/{$v}$/i", $field)) {
- return true;
- }
- }
- return false;
- }
- protected function getRemoteSelectField($fieldName, $relations = [])
- {
- $field = explode('_', $fieldName);
- foreach ($relations as $relation) {
- if ($relation['remoteSelectField'] && $relation['relationTableTypeName'] == $field[0]) {
- return $relation['remoteSelectField'];
- }
- }
- foreach ($this->remoteSelectFieldMap as $field => $item) {
- if (in_array($fieldName, $item)) {
- return $field;
- }
- }
- return 'name';
- }
- protected function getModelAttrMethod(&$modelSetAttrArr, $field, $inputType, $column)
- {
- $fieldName = ucfirst($this->getCamelizeName($field));
- if ($inputType == 'switch') {
- $modelSetAttrArr[] = $this->stub->getReplacedStub('modelAttr' . DIRECTORY_SEPARATOR . 'setSwitch', [
- 'field' => $fieldName
- ]);
- } elseif (in_array($inputType, ['checkbox', 'selects', 'remoteSelects', 'city', 'images', 'files'])) {
- $modelSetAttrArr[] = $this->stub->getReplacedStub('modelAttr' . DIRECTORY_SEPARATOR . 'stringToArrayMethod', [
- 'field' => $fieldName
- ]);
- $modelSetAttrArr[] = $this->stub->getReplacedStub('modelAttr' . DIRECTORY_SEPARATOR . 'arrayToStringMethod', [
- 'field' => $fieldName
- ]);
- } elseif ($inputType == 'array') {
- $modelSetAttrArr[] = $this->stub->getReplacedStub('modelAttr' . DIRECTORY_SEPARATOR . 'getArray', [
- 'field' => $fieldName
- ]);
- } elseif (in_array($inputType, ['textarea', 'remoteSelect'])) {
- $modelSetAttrArr[] = $this->stub->getReplacedStub('modelAttr' . DIRECTORY_SEPARATOR . 'defaultEmptyStringMethod', [
- 'field' => $fieldName
- ]);
- } elseif ($inputType == 'time') {
- $modelSetAttrArr[] = $this->stub->getReplacedStub('modelAttr' . DIRECTORY_SEPARATOR . 'setTime', [
- 'field' => $fieldName
- ]);
- } elseif ($inputType == 'editor') {
- $modelSetAttrArr[] = $this->stub->getReplacedStub('modelAttr' . DIRECTORY_SEPARATOR . 'getEditor', [
- 'field' => $fieldName
- ]);
- }
- }
- protected function getCamelizeName($uncamelized_words, $separator = '_')
- {
- $uncamelized_words = $separator . str_replace($separator, " ", strtolower($uncamelized_words));
- return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator);
- }
- protected function getRemoteSelectUrl($fieldName, $relations, $webControllerUrls)
- {
- $fieldName = explode('_', $fieldName);
- $defaultValue = [
- 'importControllerUrls' => false,
- 'url' => $fieldName[0],
- 'pk' => $fieldName[0] . '.id',
- ];
- $relationFile = false;
- foreach ($relations as $relation) {
- if ($relation['relationTableTypeName'] == $fieldName[0]) {
- $relationFile = $relation['relationFile'];
- $defaultValue['pk'] = $fieldName[0] . '.' . $relation['relationPrimaryKey'];
- }
- }
- if (!$relationFile) {
- return $defaultValue;
- }
- // 在关联模型中获取控制器url名称
- $preg = "/@controllerUrl '(.+?)'/i";
- $controllerUrlName = $this->getPregValueFromFile($relationFile, $preg);
- if (!$controllerUrlName) {
- return $defaultValue;
- }
- // 确定$webControllerUrls中存在该url的定义
- $preg = '/(' . $controllerUrlName . ')/is';
- $existWebControllerUrls = $this->getPregValueFromFile($webControllerUrls, $preg);
- if (!$existWebControllerUrls) {
- return $defaultValue;
- }
- return [
- 'importControllerUrls' => $controllerUrlName,
- 'url' => $controllerUrlName . " + 'index'",
- 'pk' => $defaultValue['pk'],
- ];
- }
- public function getPregValueFromFile($file, $preg, $valIdx = 1)
- {
- $fileContent = @file_get_contents($file);
- if (!$fileContent) {
- return false;
- }
- preg_match($preg, $fileContent, $result);
- if ($result && isset($result[$valIdx])) {
- return $result[$valIdx];
- }
- return false;
- }
- /**
- * 列替换数据
- */
- public function getColumnReplaceData($column, $fieldName, $inputType)
- {
- $columnData = [];
- if (in_array($column['DATA_TYPE'], ['enum', 'set', 'tinyint', 'char'])) {
- if ($column['DATA_TYPE'] !== 'tinyint') {
- $columnData = substr($column['COLUMN_TYPE'], strlen($column['DATA_TYPE']) + 1, -1);
- $columnData = explode(',', str_replace("'", '', $columnData));
- }
- $columnData = $this->getItemArray($columnData, $fieldName, $column['COLUMN_COMMENT']);
- }
- if (!$columnData && in_array($inputType, ['select', 'selects'])) {
- $columnData = $this->getItemArray($columnData, $fieldName, $column['COLUMN_COMMENT']);
- }
- if ($columnData) {
- foreach ($columnData as $key => $columnDatum) {
- $columnData[$key] = "t('" . $this->langPrefix . $columnDatum . "')";
- }
- }
- return $columnData;
- }
- }
|