| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165 |
- <?php
- namespace plugin\admin\app\common;
- use plugin\admin\app\common\Util;
- use support\exception\BusinessException;
- class Layui
- {
- /**
- * 生成的html代码
- * @var string
- */
- protected $htmlContent = '';
- /**
- * 生成的js代码
- * @var string
- */
- protected $jsContent = '';
- /**
- * 获取生成的html代码
- * @param $indent
- * @return string
- */
- public function html($indent = 0): string
- {
- return str_replace("\n", "\n" . str_repeat(' ', $indent), $this->htmlContent);
- }
- /**
- * 获取生成的js代码
- * @param $indent
- * @return string
- */
- public function js($indent = 0): string
- {
- return str_replace("\n", "\n" . str_repeat(' ', $indent), $this->jsContent);
- }
- /**
- * 获取控件及相关参数
- * @param $options
- * @return array
- */
- protected function options($options): array
- {
- array_walk_recursive($options, function(&$item, $key){
- if (is_string($item)) {
- $item = htmlspecialchars($item);
- if ($key === 'url') {
- $item = str_replace('&', '&', $item);
- }
- }
- });
- $field = $options['field']??'';
- $props = !empty($options['props']) ? $options['props'] : [];
- $verify_string = !empty($props['lay-verify']) ? ' lay-verify="'.$props['lay-verify'].'"' : '';
- $required_string = strpos($verify_string, 'required') ? ' required' : '';
- $label = !empty($options['label']) ? '<label class="layui-form-label'.$required_string.'">'.$options['label'].'</label>' : '';
- $value = $props['value'] ?? '';
- $class = $props['class'] ?? 'layui-input-block';
- return [$label, $field, $value, $props, $verify_string, $required_string, $class];
- }
- /**
- * input输入框
- * @param $options
- * @return void
- */
- public function input($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $placeholder_string = !empty($props['placeholder']) ? ' placeholder="'.$props['placeholder'].'"' : '';
- $autocomplete_string = !empty($props['autocomplete']) ? ' autocomplete="'.$props['autocomplete'].'"' : '';
- $disabled_string = !empty($props['disabled']) ? ' disabled' : '';
- $type = $props['type'] ?? 'text';
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <input type="$type" name="$field" value="$value"$disabled_string$required_string$verify_string$placeholder_string$autocomplete_string class="layui-input">
- </div>
- </div>
- EOF;
- }
- /**
- * input数字输入框
- * @param $options
- * @return void
- */
- public function inputNumber($options)
- {
- $options['props']['type'] = 'number';
- $this->input($options);
- }
- /**
- * 输入框范围
- * @param $options
- * @return void
- */
- public function inputRange($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $type = $props['type'] ?? 'text';
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <div class="layui-input-block">
- <input type="$type" autocomplete="off" name="{$field}[]" class="layui-input inline-block" placeholder="开始">
- -
- <input type="$type" autocomplete="off" name="{$field}[]" class="layui-input inline-block" placeholder="结束">
- </div>
- </div>
- </div>
- EOF;
- }
- /**
- * 输入框模糊查询
- * @param $options
- * @return void
- */
- public function inputLike($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $type = $props['type'] ?? 'text';
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <div class="layui-input-block">
- <input type="hidden" autocomplete="off" name="{$field}[]" value="like" class="layui-input inline-block">
- <input type="$type" autocomplete="off" name="{$field}[]" class="layui-input">
- </div>
- </div>
- </div>
- EOF;
- }
- /**
- * 数字输入框范围
- * @param $options
- * @return void
- */
- public function inputNumberRange($options)
- {
- $options['props']['type'] = 'number';
- $this->inputRange($options);
- }
- /**
- * 数字输入框模糊查询
- * @param $options
- * @return void
- */
- public function inputNumberLike($options)
- {
- $options['props']['type'] = 'number';
- $this->inputLike($options);
- }
- /**
- * 密码输入框
- * @param $options
- * @return void
- */
- public function inputPassword($options)
- {
- $options['props']['type'] = 'password';
- $this->input($options);
- }
- /**
- * 文本域
- * @param $options
- * @return void
- */
- public function textArea($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $placeholder_string = !empty($props['placeholder']) ? ' placeholder="'.$props['placeholder'].'"' : '';
- $disabled_string = !empty($props['disabled']) ? ' disabled' : '';
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <textarea name="$field"$required_string$verify_string$placeholder_string$disabled_string class="layui-textarea">$value</textarea>
- </div>
- </div>
- EOF;
- }
- /**
- * 富文本
- * @param $options
- * @return void
- */
- public function richText($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $placeholder_string = !empty($props['placeholder']) ? ' placeholder="'.$props['placeholder'].'"' : '';
- $disabled_string = !empty($props['disabled']) ? ' disabled' : '';
- $id = $field;
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <textarea id="$id" name="$field"$required_string$verify_string$placeholder_string$disabled_string class="layui-textarea">$value</textarea>
- </div>
- </div>
- EOF;
- $options_string = '';
- if (!isset($props['images_upload_url'])) {
- $props['images_upload_url'] = '/app/admin/upload/image';
- }
- $props = $this->prepareProps($props);
- $options_string .= "\n" . $this->preparePropsToJsObject($props, 1, true);
- $this->jsContent .= <<<EOF
- // 字段 {$options['label']} $field
- layui.use(["tinymce"], function() {
- var tinymce = layui.tinymce
- var edit = tinymce.render({
- elem: "#$id",$options_string
- });
- edit.on("blur", function(){
- layui.$("#$id").val(edit.getContent());
- });
- });
- EOF;
- }
- /**
- * json编辑框
- * @param $options
- * @return void
- */
- public function jsonEditor($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $placeholder_string = !empty($props['placeholder']) ? ' placeholder="'.$props['placeholder'].'"' : '';
- $autocomplete_string = !empty($props['autocomplete']) ? ' autocomplete="'.$props['autocomplete'].'"' : '';
- $disabled_string = !empty($props['disabled']) ? ' disabled' : '';
- $type = $props['type'] ?? 'text';
- if (empty($value)){
- $value='{}';
- }
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <input type="$type" name="$field"id="$field" value="$value"$disabled_string$required_string$verify_string$placeholder_string$autocomplete_string class="layui-input">
- </div>
- </div>
- EOF;
- $this->jsContent .= <<<EOF
- jsonArea({
- el: "#$field",
- change: function(data) {
- console.log(data);
- }
- });
- EOF;
- }
- /**
- * 上传组件
- * @param $options
- * @return void
- */
- public function upload($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $props['accept'] = $props['accept'] ?? 'file';
- $props['url'] = $props['url'] ?? '/app/admin/upload/file';
- $id = $this->createId($field);
- $props['field'] = $props['field'] ?? '__file__';
- unset($props['lay-verify']);
- $options_string = '';
- $props = $this->prepareProps($props);
- $options_string .= "\n" . $this->preparePropsToJsObject($props, 1, true);
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <span>$value</span>
- <input type="text" style="display:none" name="$field" value="$value" />
- <button type="button" class="pear-btn pear-btn-primary pear-btn-sm" id="$id" permission="app.admin.upload.file">
- <i class="layui-icon layui-icon-upload"></i>上传文件
- </button>
- <button type="button" class="pear-btn pear-btn-primary pear-btn-sm" id="attachment-choose-$id" permission="app.admin.upload.attachment">
- <i class="layui-icon layui-icon-align-left"></i>选择文件
- </button>
- </div>
- </div>
- EOF;
- $this->jsContent .= <<<EOF
- // 字段 {$options['label']} $field
- layui.use(["upload", "layer", "popup", "util"], function() {
- let input = layui.$("#$id").prev();
- input.prev().html(layui.util.escape(input.val()));
- layui.$("#attachment-choose-$id").on("click", function() {
- parent.layer.open({
- type: 2,
- title: "选择附件",
- content: "/app/admin/upload/attachment",
- area: ["95%", "90%"],
- success: function (layero, index) {
- parent.layui.$("#layui-layer" + index).data("callback", function (data) {
- input.val(data.url).prev().html(layui.util.escape(data.url));
- });
- }
- });
- });
- layui.upload.render({
- elem: "#$id",$options_string
- done: function (res) {
- if (res.code) return layui.popup.failure(res.msg);
- this.item.prev().val(res.data.url).prev().html(layui.util.escape(res.data.url));
- }
- });
- });
- EOF;
- }
- /**
- * 图片上传组件
- * @param $options
- * @return void
- */
- public function uploadImage($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $props['acceptMime'] = $props['acceptMime'] ?? 'image/gif,image/jpeg,image/jpg,image/png';
- $props['url'] = $props['url'] ?? '/app/admin/upload/image';
- $id = $this->createId($field);
- unset($props['lay-verify']);
- $props['field'] = $props['field'] ?? '__file__';
- $options_string = '';
- $props = $this->prepareProps($props);
- $options_string .= "\n" . $this->preparePropsToJsObject($props, 1, true);
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <img class="img-3" src=""/>
- <input type="text" style="display:none" name="$field" value="$value" />
- <button type="button" class="pear-btn pear-btn-primary pear-btn-sm" id="$id" permission="app.admin.upload.image">
- <i class="layui-icon layui-icon-upload"></i>上传图片
- </button>
- <button type="button" class="pear-btn pear-btn-primary pear-btn-sm" id="attachment-choose-$id" permission="app.admin.upload.attachment">
- <i class="layui-icon layui-icon-align-left"></i>选择图片
- </button>
- </div>
- </div>
- EOF;
- $this->jsContent .= <<<EOF
- // 字段 {$options['label']} $field
- layui.use(["upload", "layer"], function() {
- let input = layui.$("#$id").prev();
- input.prev().attr("src", input.val());
- layui.$("#attachment-choose-$id").on("click", function() {
- parent.layer.open({
- type: 2,
- title: "选择附件",
- content: "/app/admin/upload/attachment?ext=jpg,jpeg,png,gif,bmp",
- area: ["95%", "90%"],
- success: function (layero, index) {
- parent.layui.$("#layui-layer" + index).data("callback", function (data) {
- input.val(data.url).prev().attr("src", data.url);
- });
- }
- });
- });
- layui.upload.render({
- elem: "#$id",$options_string
- done: function (res) {
- if (res.code > 0) return layui.layer.msg(res.msg);
- this.item.prev().val(res.data.url).prev().attr("src", res.data.url);
- }
- });
- });
- EOF;
- }
- /**
- * 日期时间选择组件
- * @param $options
- * @return void
- */
- public function dateTimePicker($options)
- {
- $options['props']['type'] = 'datetime';
- $this->datePicker($options);
- }
- /**
- * 日期选择组件
- * @param $options
- * @return void
- */
- public function datePicker($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $value_string = $value ? ' value="'.$value.'"' : '';
- $options_string = '';
- unset($props['required'], $props['lay-verify'], $props['value']);
- $props = $this->prepareProps($props);
- $options_string .= "\n" . $this->preparePropsToJsObject($props, 1, true);
- $id = $this->createId($field);
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <input type="text" name="$field" id="$id"$value_string$required_string$verify_string autocomplete="off" class="layui-input">
- </div>
- </div>
- EOF;
- $this->jsContent .= <<<EOF
- // 字段 {$options["label"]} $field
- layui.use(["laydate"], function() {
- layui.laydate.render({
- elem: "#$id",$options_string
- });
- })
- EOF;
- }
- /**
- * 日期时间范围选择组件
- * @param $options
- * @return void
- */
- public function dateTimePickerRange($options)
- {
- $options['props']['type'] = 'datetime';
- $this->datePickerRange($options);
- }
- /**
- * 日期范围选择组件
- * @param $options
- * @return void
- */
- public function datePickerRange($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- if (!isset($options['props']['type'])) {
- $options['props']['type'] = 'date';
- }
- $options_string = '';
- unset($props['required'], $props['lay-verify'], $props['value']);
- $props = $this->prepareProps($props);
- $options_string .= "\n" . $this->preparePropsToJsObject($props, 1, true);
- $id = $this->createId($field);
- $id_start = "$id-date-start";
- $id_end = "$id-date-end";
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <div class="layui-input-block" id="$id">
- <input type="text" autocomplete="off" name="{$field}[]" id="$id_start" class="layui-input inline-block" placeholder="开始时间">
- -
- <input type="text" autocomplete="off" name="{$field}[]" id="$id_end" class="layui-input inline-block" placeholder="结束时间">
- </div>
- </div>
- </div>
- EOF;
- $this->jsContent .= <<<EOF
- // 字段 {$options['label']} $field
- layui.use(["laydate"], function() {
- layui.laydate.render({
- elem: "#$id",
- range: ["#$id_start", "#$id_end"],$options_string
- });
- })
- EOF;
- }
- /**
- * 创建id
- * @param $field
- * @return mixed
- */
- protected function createId($field)
- {
- return $field;
- }
- /**
- * 图标选择组件
- * @param $options
- * @return void
- */
- public function iconPicker($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $value_string = $value ? ' value="'.$value.'"' : '';
- $id = $this->createId($field);
- $options_string = '';
- $props = $this->prepareProps($props);
- $options_string .= "\n" . $this->preparePropsToJsObject($props, 1, true);
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <input name="$field" id="$id"$value_string$required_string$verify_string />
- </div>
- </div>
- EOF;
- $this->jsContent .= <<<EOF
- // 字段 {$options['label']} $field
- layui.use(["iconPicker"], function() {
- layui.iconPicker.render({
- elem: "#$id",
- type: "fontClass",
- page: false,$options_string
- });
- });
- EOF;
- }
- /**
- * switch组件
- * @param $options
- * @return void
- */
- public function switch($options)
- {
- [$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $value = (int)$value;
- $disabled_string = !empty($props['disabled']) ? ' disabled' : '';
- $lay_text = !empty($props['lay-text']) ? "lay-text=\"{$props['lay-text']}\"" : '';
- $id = $this->createId($field);
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $label
- <div class="$class">
- <input type="checkbox" id="$id" lay-filter="$field"$disabled_string$required_string lay-skin="switch" $lay_text/>
- <input type="text" style="display:none" name="$field" value="$value"$required_string />
- </div>
- </div>
- EOF;
- $this->jsContent .= <<<EOF
- // 字段 {$options['label']} $field
- layui.use(["form"], function() {
- layui.$("#$id").attr("checked", layui.$('input[name="$field"]').val() != 0);
- layui.form.render();
- layui.form.on("switch($field)", function(data) {
- layui.$('input[name="$field"]').val(this.checked ? 1 : 0);
- });
- })
- EOF;
- }
- /**
- * 下拉选择组件
- * @return void
- */
- public function select($options)
- {
- $options['props']['model'] = array_merge_recursive([
- 'icon' => 'hidden',
- 'label' => [
- 'type' => 'text',
- ]
- ], $options['props']['model'] ?? []);
- $options['props']['clickClose'] = $options['props']['clickClose'] ?? true;
- $options['props']['radio'] = $options['props']['radio'] ?? true;
- $this->apiSelect($options);
- }
- /**
- * 下拉多选组件
- * @return void
- */
- public function selectMulti($options)
- {
- $options['props']['toolbar'] = array_merge_recursive([
- 'show' => true,
- 'list' => [ 'ALL', 'CLEAR', 'REVERSE' ]
- ], $options['props']['toolbar'] ?? []);
- $this->apiSelect($options);
- }
- /**
- * 树单选组件
- * @return void
- */
- public function treeSelect($options)
- {
- $options['props']['model'] = array_merge_recursive([
- 'icon' => 'hidden',
- 'label' => [
- 'type' => 'text',
- ]
- ], $options['props']['model'] ?? []);
- $options['props']['clickClose'] = $options['props']['clickClose'] ?? true;
- $options['props']['radio'] = $options['props']['radio'] ?? true;
- $options['props']['tree'] = array_merge_recursive([
- '$show' => true,
- '$strict' => false,
- '$clickCheck' => true,
- '$clickExpand' => false,
- '$expandedKeys' => '$initValue'
- ], $options['props']['tree'] ?? []);
- $this->apiSelect($options);
- }
- /**
- * 树多选组件
- * @return void
- */
- public function treeSelectMulti($options)
- {
- $options['props']['tree'] = array_merge_recursive(['show' => true,
- '$expandedKeys' => '$initValue'], $options['props']['tree'] ?? []);
- $options['props']['toolbar'] = array_merge_recursive([
- '$show' => true,
- '$list' => [ 'ALL', 'CLEAR', 'REVERSE' ]
- ], $options['props']['toolbar'] ?? []);
- $this->apiSelect($options);
- }
- /**
- * 选择框,支持单选、多选、树形选择
- * @see https://maplemei.gitee.io/xm-select/
- * @param $options
- * @return void
- */
- public function apiSelect($options)
- {
- [$select_label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
- $default_value_string = isset($props['initValue']) && $props['initValue'] != '' ? $props['initValue'] : $value;
- $url = $props['url'] ?? '';
- $options_string = '';
- if (isset($props['lay-verify'])) {
- $props['layVerify'] = $props['lay-verify'];
- }
- unset($props['lay-verify'], $props['url']);
- foreach ($props as $key => $item) {
- if (is_array($item)) {
- $item = json_encode($item, JSON_UNESCAPED_UNICODE);
- $item = preg_replace('/"\$([^"]+)"/', '$1', $item);
- $options_string .= "\n".($url?' ':' ')."$key: $item,";
- } else if (is_string($item)) {
- $options_string .= "\n".($url?' ':' ')."$key: \"$item\",";
- } else {
- $options_string .= "\n".($url?' ':' ')."$key: ".var_export($item, true).",";
- }
- }
- $id = $this->createId($field);
- if ($url) {
- $this->jsContent .= <<<EOF
- // 字段 {$options['label']} $field
- layui.use(["jquery", "xmSelect", "popup"], function() {
- layui.$.ajax({
- url: "$url",
- dataType: "json",
- success: function (res) {
- let value = layui.$("#$id").attr("value");
- let initValue = value ? value.split(",") : [];
- layui.xmSelect.render({
- el: "#$id",
- name: "$field",
- initValue: initValue,
- filterable: true,
- data: res.data, $options_string
- });
- if (res.code) {
- layui.popup.failure(res.msg);
- }
- }
- });
- });
- EOF;
- } else {
- $this->jsContent .= <<<EOF
- // 字段 {$options['label']} $field
- layui.use(["jquery", "xmSelect"], function() {
- let value = layui.$("#$id").attr("value");
- let initValue = value ? value.split(",") : [];
- layui.xmSelect.render({
- el: "#$id",
- name: "$field",
- filterable: true,
- initValue: initValue,$options_string
- })
- });
- EOF;
- }
- $this->htmlContent .= <<<EOF
- <div class="layui-form-item">
- $select_label
- <div class="$class">
- <div name="$field" id="$id"$required_string value="$default_value_string" ></div>
- </div>
- </div>
- EOF;
- }
- /**
- * 构建表单
- * @param $table
- * @param string $type
- * @return Layui
- * @throws BusinessException
- */
- public static function buildForm($table, string $type = 'insert'): Layui
- {
- if (!in_array($type, ['insert', 'update', 'search'])) {
- $type = 'insert';
- }
- $filter = $type === 'search' ? 'searchable' : 'form_show';
- $form = new Layui();
- $schema = Util::getSchema($table);
- $forms = $schema['forms'];
- $columns = $schema['columns'];
- $primary_key = $schema['table']['primary_key'][0] ?? null;
- foreach ($forms as $key => $info) {
- if (empty($info[$filter])) {
- continue;
- }
- $field = $info['field'];
- $default = $columns[$key]['default'];
- $control = strtolower($info['control']);
- $auto_increment = $columns[$key]['auto_increment'];
- // 搜索框里上传组件替换为input
- if ($type == 'search' && in_array($control, ['upload', 'uploadimg'])) {
- $control = 'input';
- $info['control_args'] = '';
- }
- if ($type === 'search' && $control === 'switch') {
- $control = 'select';
- if (preg_match('/lay-text:(.+?)\|([^;]+)/', $info['control_args'], $matches)) {
- $info['control_args'] = 'data:1:' . $matches[1] . ',0:' . $matches[2];
- } else {
- $info['control_args'] = 'data:1:是,0:否';
- }
- }
- $props = Util::getControlProps($control, $info['control_args']);
- // 增加修改记录验证必填项
- if ($filter == 'form_show' && !$columns[$key]['nullable'] && $default === null && ($field !== 'password' || $type === 'insert')) {
- if (!isset($props['lay-verify'])) {
- $props['lay-verify'] = 'required';
- // 非类似字符串类型不允许传空
- } elseif (!in_array($columns[$key]['type'], ['string', 'text', 'mediumText', 'longText', 'char', 'binary', 'json'])
- && strpos($props['lay-verify'], 'required') === false) {
- $props['lay-verify'] = 'required|' . $props['lay-verify'];
- }
- }
- // 增加记录显示默认值
- if ($type === 'insert' && !isset($props['value']) && $default !== null) {
- $props['value'] = $default;
- }
- // 主键是自增字段或者表单是更新类型不显示主键
- if ($primary_key && $field == $primary_key && (($type == 'insert' && $auto_increment) || $type == 'update')) {
- continue;
- }
- // 查询类型
- if ($type == 'search') {
- if ($info['search_type'] == 'between' && method_exists($form, "{$control}Range")) {
- $control = "{$control}Range";
- } elseif ($info['search_type'] == 'like' && method_exists($form, "{$control}Like")) {
- $control = "{$control}Like";
- }
- }
- // 查询类型移除lay-verify
- if ($type == 'search' && !empty($props['lay-verify'])) {
- $props['lay-verify'] = '';
- }
- $options = [
- 'label' => $info['comment'] ?: $field,
- 'field' => $field,
- 'props' => $props,
- ];
- $form->{$control}($options);
- }
- return $form;
- }
- /**
- * 构建表格
- * @param $table
- * @param int $indent
- * @return array|string|string[]
- * @throws BusinessException
- */
- public static function buildTable($table, int $indent = 0)
- {
- $schema = Util::getSchema($table);
- $forms = $schema['forms'];
- $codes = '';
- $cols = '';
- $api = '';
- $api_result = '';
- foreach ($forms as $info) {
- $title = $info['comment'] ?: $info['field'];
- $hide_str = $info['list_show'] ? '' : "\n hide: true,";
- $sort_str = $info['enable_sort'] ? "\n sort: true," : '';
- $field = $info['field'];
- $templet = '';
- $schema = <<<EOF
- title: "$title",align: "center",
- field: "$field",$hide_str$sort_str
- EOF;
- $control = strtolower($info['control']);
- switch ($control) {
- case 'switch':
- $props = Util::getControlProps($info['control'], $info['control_args']);
- $lay_text = $props['lay-text'] ?? '';
- $templet = <<<EOF
- templet: function (d) {
- let field = "$field";
- form.on("switch("+field+")", function (data) {
- let load = layer.load();
- let postData = {};
- postData[field] = data.elem.checked ? 1 : 0;
- postData[PRIMARY_KEY] = this.value;
- $.post(UPDATE_API, postData, function (res) {
- layer.close(load);
- if (res.code) {
- return layui.popup.failure(res.msg, function () {
- data.elem.checked = !data.elem.checked;
- form.render();
- });
- }
- return layui.popup.success("操作成功");
- })
- });
- let checked = d[field] === 1 ? "checked" : "";
- return '<input type="checkbox" value="'+util.escape(d[PRIMARY_KEY])+'" lay-filter="'+util.escape(field)+'" lay-skin="switch" lay-text="'+util.escape('$lay_text')+'" '+checked+'/>';
- }
- EOF;
- break;
- case 'iconpicker':
- $templet = <<<EOF
- templet: function (d) {
- return '<i class="layui-icon ' + util.escape(d['$field']) + '"></i>';
- }
- EOF;
- break;
- case 'upload':
- $templet = <<<EOF
- templet: function (d) {
- return '<a href="' + encodeURI(d['$field']) + '" target="_blank">' + util.escape(d['$field']) + '</a>';
- }
- EOF;
- break;
- case 'uploadimage':
- $templet = <<<EOF
- templet: function (d) {
- return '<img src="'+encodeURI(d['$field'])+'" style="max-width:32px;max-height:32px;" alt="" />'
- }
- EOF;
- break;
- }
- if (in_array($control, ['select', 'selectmulti', 'treeselect', 'treeselectmulti'])) {
- $props = Util::getControlProps($info['control'], $info['control_args']);
- if (isset($props['url'])) {
- $api .= "\napis.push([\"$field\", \"{$props['url']}\"]);";
- $api_result .= "\napiResults[\"$field\"] = [];";
- } else if (!empty($props['data'])) {
- $options = [];
- foreach ($props['data'] as $option) {
- if (isset($option['value']) && isset($option['name'])) {
- $options[$option['value']] = $option['name'];
- }
- }
- $api_result .= "\napiResults[\"$field\"] = " . json_encode($options, JSON_UNESCAPED_UNICODE) . ";";
- } else {
- $api_result .= "\napiResults[\"$field\"] = [];";
- }
- $templet = <<<EOF
- templet: function (d) {
- let field = "$field";
- if (typeof d[field] == "undefined") return "";
- let items = [];
- layui.each((d[field] + "").split(","), function (k , v) {
- items.push(apiResults[field][v] || v);
- });
- return util.escape(items.join(","));
- }
- EOF;
- }
- $cols .= <<<EOF
- ,{
- $schema$templet
- }
- EOF;
- }
- $cols = <<<EOF
- // 表头参数
- let cols = [
- {
- type: "checkbox",
- align: "center"
- }$cols,{
- title: "操作",
- toolbar: "#table-bar",
- align: "center",
- fixed: "right",
- width: 120,
- }
- ];
- EOF;
- if (!$api && $api_result) {
- $codes = <<<EOF
- // 获取表格中下拉或树形组件数据
- let apiResults = {};$api_result
- EOF;
- } else if ($api && !$api_result) {
- $codes = <<<EOF
- // 获取表格中下拉或树形组件数据
- let apis = [];$api
- EOF;
- } else if ($api && $api_result) {
- $codes = <<<EOF
- // 获取表格中下拉或树形组件数据
- let apis = [];$api
- let apiResults = {};$api_result
- EOF;
- }
- if ($api) {
- $codes = <<<EOF
- $cols
- // 渲染表格
- function render()
- {
- table.render({
- elem: "#data-table",
- url: SELECT_API,
- page: true,
- cols: [cols],
- skin: "line",
- size: "lg",
- toolbar: "#table-toolbar",
- autoSort: false,
- defaultToolbar: [{
- title: "刷新",
- layEvent: "refresh",
- icon: "layui-icon-refresh",
- }, "filter", "print", "exports"],
- done: function () {
- layer.photos({photos: 'div[lay-id="data-table"]', anim: 5});
- }
- });
- }
- $codes
- let count = apis.length;
- layui.each(apis, function (k, item) {
- let [field, url] = item;
- $.ajax({
- url: url,
- dateType: "json",
- success: function (res) {
- if (res.code) {
- return layui.popup.failure(res.msg);
- }
- function travel(items) {
- for (let k in items) {
- let item = items[k];
- apiResults[field][item.value] = item.name;
- if (item.children) {
- travel(item.children);
- }
- }
- }
- travel(res.data);
- },
- complete: function () {
- if (--count === 0) {
- render();
- }
- }
- });
- });
- if (!count) {
- render();
- }
- EOF;
- } else {
- $codes = <<<EOF
- $cols
- // 渲染表格
- table.render({
- elem: "#data-table",
- url: SELECT_API,
- page: true,
- cols: [cols],
- skin: "line",
- size: "lg",
- toolbar: "#table-toolbar",
- autoSort: false,
- defaultToolbar: [{
- title: "刷新",
- layEvent: "refresh",
- icon: "layui-icon-refresh",
- }, "filter", "print", "exports"],
- done: function () {
- layer.photos({photos: 'div[lay-id="data-table"]', anim: 5});
- }
- });
- $codes
- EOF;
- }
- return str_replace("\n", "\n" . str_repeat(' ', $indent), $codes);
- }
- /**
- * 预处理props
- */
- private function prepareProps($props)
- {
- $raw_list = ['true','false','null','undefined'];
- foreach ($props as $k => $v) {
- if (is_array($v)) {
- $props[$k] = $this->prepareProps($v);
- } elseif (!in_array($v, $raw_list) && !is_numeric($v)) {
- if (strpos($v, "#") === 0){
- $props[$k] = substr($v, 1);
- } else {
- $props[$k] = "\"$v\"";
- }
- }
- }
- return $props;
- }
- private function preparePropsToJsObject($props, $indent = 0, $sub = false)
- {
- $string = '';
- $indent_string = str_repeat(' ', $indent);
- if (!$sub) {
- $string .= "$indent_string{\n";
- }
- foreach ($props as $k => $v) {
- if (!preg_match("#^[a-zA-Z0-9_]+$#", $k)) {
- $k = "'$k'";
- }
- if (is_array($v)) {
- $string .= "$indent_string $k: {\n{$this->preparePropsToJsObject($v, $indent + 1, true)}\n$indent_string },\n";
- } else {
- $string .= "$indent_string $k: $v,\n";
- }
- }
- if (!$sub) {
- $string .= "$indent_string}\n";
- }
- return trim($string,"\n");
- }
- }
|