*/ namespace sdModule\layui\defaultForm; use app\common\SdException; use sdModule\layui\defaultForm\formUnit\AuxTitle; use sdModule\layui\defaultForm\formUnit\Selects; use sdModule\layui\defaultForm\formUnit\UEditor; use sdModule\layui\defaultForm\formUnit\UnitBase; /** * Class Form * @package sdModule\layui\defaultForm */ class Form { private const UNIT_CLASS = "\\sdModule\\layui\\defaultForm\\formUnit\\%s"; private const FORM_TEMPLATE = '
%s
%s
%s
'; private const ITEM_TEMPLATE = '
%s
'; private const LABEL_TEMPLATE = ''; private const TIP_TEMPLATE = '
%s
'; private const SCRIPT_TEMPLATE = ''; private const SUBMIT_TEMPLATE = '
'; /** * @var array 创建表单的数据 */ private array $form_data = []; /** * @var array 表单默认值数据 */ private array $default_data = []; /** * @var array 表单元素 */ private array $unit = []; /** * @var array js代码 */ private array $js = []; /** * @var array 加载的js */ private array $load_js = []; /** * @var int 页面占比1 - 12 */ private int $custom_md = 0; /** * @var array 验证规则 */ private array $verify_rule = []; /** * @var array 短表单及提示语, key = name, value = 提示, eg: ['city' => '请选择城市'] */ private array $short_from_and_tip = []; /** * @var string 场景 */ private string $scene; /** * @var string 风格 */ private string $skin = ''; /** * 创建表单 * @param array $form_data 创建表单数据 * @param string $scene 场景值 * @return Form */ public static function create(array $form_data, string $scene = '') { $form = new self(); $form->form_data = $form_data; $form->scene = $scene; return $form; } /** * 设置表单风格 * @param bool $pane * @return $this */ public function setSkinToPane($pane = false) { $this->skin = $pane ? "layui-form-pane" : ""; return $this; } /** * 获取表单风格 * @return string */ public function getSkin() { return $this->skin; } /** * 完成创建 * @return $this * @throws SdException * @throws \ReflectionException */ public function complete() { $this->makeUnitCode(); $this->form_data = $this->fieldMerge(); return $this; } /** * 设置占用列 * @param int $custom_md * @return Form */ public function setCustomMd(int $custom_md = 12) { if (in_array($custom_md, range(1, 12))){ $this->custom_md = $custom_md; } return $this; } /** * 设置短标签 * @param array $tips key = name, value = 提示, eg: ['city' => '请选择城市'] * @return Form */ public function setShortFrom(array $tips) { $this->short_from_and_tip = $tips; return $this; } /** * 引入外部js * @param $js_path * @return $this */ public function importJs($js_path) { $this->load_js[] = $this->assets($js_path); return $this; } /** * 获取html * @throws SdException * @throws \ReflectionException */ private function makeUnitCode() { foreach ($this->form_data as $build_index => $item) { if ($item instanceof \Closure){ // 处理行内标签 foreach (call_user_func($item) as $items) { $this->codeGenerate($items, $build_index); } }elseif(is_array($item)){ // 处理表格表单 $this->tableCode($item, $build_index); }else{ $this->codeGenerate($item); } } } /** * @param $item * @param $build_index * @throws SdException * @throws \ReflectionException */ private function tableCode($item, $build_index) { $code = []; // 整个表格的代码 foreach ($item as $items) { $line = []; // 一行的代码 foreach ($items as $name => $item_){ $attr = ''; if (is_array($item_)){ list($item_, $attr) = $item_; } if ($item_ instanceof UnitData){ $item_ = $this->codeGenerate($item_, "T_{$build_index}"); $item_[array_key_first($item_)] = [current($item_), $attr]; $line = array_merge($line, $item_); }else{ $line[] = [$item_, $attr]; } } $code[] = $line; } $key = is_string($build_index) ? $build_index : "T_{$build_index}"; $this->unit[$key] = $code; } /** * 代码生成 * @param UnitData $unit_item 表单项目数据 * @param null|int $build_index 组合表单的索引 * @return mixed|string|void * @throws SdException * @throws \ReflectionException */ private function codeGenerate(UnitData $unit_item, $build_index = null) { if (in_array($this->scene, $unit_item->get('remove_scene'))) { return; } $unit = $this->getUnitInstance($unit_item); $name = $unit_item->get('name'); $unit->setData($unit_item->get('select_data')) ->setPreset($unit_item->get('preset')) ->setConfig($unit_item->get('config')); if (property_exists($unit, 'default') && isset($this->default_data[$name])) { $unit->default = $this->default_data[$name]; } $this->js[$name] = $unit->getJs(); if ($unit instanceof UEditor){ $this->load_js['u-e_config'] = sprintf(self::SCRIPT_TEMPLATE, $this->assets("/admin_static/editor/ueditor.config.js")); $this->load_js['u-e_all'] = sprintf(self::SCRIPT_TEMPLATE, $this->assets("/admin_static/editor/ueditor.all.js")); }elseif ($unit instanceof Selects){ $this->load_js['xm-selects'] = sprintf(self::SCRIPT_TEMPLATE, $this->assets("/admin_static/layui/dist/xm-select.js")); } $attr = $unit_item->get('input_attr')[$this->scene] ?? ''; $common_attr = $unit_item->get('input_attr')['-'] ?? ''; $attr = implode(' ', [$attr, $common_attr]); if ($build_index){ if (is_string($build_index)){ return [$name => $unit->getHtml($attr)]; } $this->unit[$build_index][$name] = $unit->getHtml($attr); }else{ $this->unit[$name] = $unit->getHtml($attr); } } /** * 获取表单元素的类 * @param UnitData $unit_item * @return object|UnitBase * @throws SdException * @throws \ReflectionException */ private function getUnitInstance(UnitData $unit_item) { $class_name = sprintf(self::UNIT_CLASS, parse_name($unit_item->get('type'), 1)); if (!class_exists($class_name)){ throw new SdException("表单类型【{$unit_item->get('type')}】不存在"); } $unit = new \ReflectionClass($class_name); return $unit->newInstance($unit_item->get('name'), $unit_item->get('label'), $unit_item->get('placeholder')); } /** * 获取html内容 * @return string */ public function getHtml() { $html = ''; foreach ($this->unit as $name => $item) { if (!is_array($item)){ $html .= $this->itemHtml($name, $item); continue; } $html_item = ''; if (is_string($name)){ $html_item = $this->tableHtml($name, $item); }else{ foreach ($item as $names => $items) { $html_item .= $this->itemHtml($names, $items, true); } } $html .= sprintf(self::ITEM_TEMPLATE, $html_item); } return $html . sprintf(self::SUBMIT_TEMPLATE, lang('submit'), lang('close')); } /** * tableForm的HTML代码创建 * @param string $name * @param array $item * @return string */ private function tableHtml(string $name, array $item) { $html_item = ""; foreach ($item as $names => $items) { $html_item .= ""; foreach ($items as $name_ => $item_){ $style = isset($this->form_data[$name_]) ? '' : 'style="background-color:#f2f2f2"'; list($item_, $attr) = $item_; $style = preg_match('/style="/', $attr) ? $attr : implode(" ", [$attr, $style]); $html_item .= sprintf("", $style, $item_); } $html_item .= ""; } $html_item .= "
%s
"; $label = preg_match('/^T_.*/', $name) ? '' : $name; $is_label = preg_match('/^N_.*/', $name); return sprintf(self::FORM_TEMPLATE, 'layui-form-item', '', $is_label ? "" : sprintf(self::LABEL_TEMPLATE, $label), $is_label ? "" : 'layui-input-block', $html_item, ''); } /** * 设置js代码 * @param string $js * @return string */ public function setJs(string $js) { $this->js['custom-sc'] = $js; return $this; } /** * 获取js代码 * @return string */ public function getJs() { return implode("\n\r", [ implode("\n\r", $this->js), $this->getDefault(), $this->submitJs() ]); } /** * 需要加载的js代码 * @return string */ public function loadJs() { return implode("\r\n", $this->load_js); } /** * 表单默认值代码 * @return false|string */ private function getDefault() { $default = json_encode($this->default_data, JSON_UNESCAPED_UNICODE); return <<form_data[$name]; if (in_array($unit->get('type'), [FormData::HIDDEN, FormData::TEXT_SHORT, FormData::AUX_TITLE])){ return $item; } $item_class = $inline ? 'layui-inline' : 'layui-form-item'; $input_class = $unit->get('label') && !isset($this->short_from_and_tip[$name]) ? 'layui-input-block' : 'layui-input-inline'; $label = $unit->get('label') ? sprintf(self::LABEL_TEMPLATE, $unit->get('label')) : ""; $tip = isset($this->short_from_and_tip[$name]) ? sprintf(self::TIP_TEMPLATE, $this->short_from_and_tip[$name]) : ""; $pane = $this->skin && in_array($unit->get('type'), [FormData::SWITCH, FormData::CHECKBOX, FormData::RADIO]) ? 'pane' : ''; return sprintf(self::FORM_TEMPLATE, $item_class, $pane ,$label, $input_class, $item, $tip); } /** * 资源路径组合 * @param $src * @return string */ private function assets($src) { return rtrim(strtr(dirname($_SERVER['SCRIPT_NAME']), ['\\' => '/']), '/') . $src; } /** * 提交的js代码 * @return string */ private function submitJs() { $success = lang("success"); $close = request()->get('__sc_tab__') ? "custom.closeTabsPage()" : "parent.layer.closeAll()"; return <<custom_md; } /** * 字段合并 * @param array $data * @return array */ private function fieldMerge(array $data = null) { $inline_field = []; $data = $data ?: $this->form_data; foreach ($data as $key => $item) { if ($item instanceof \Closure) { $inline_field = array_merge(call_user_func([$this, 'fieldMerge'], $item()), $inline_field); unset($data[$key]); } elseif (is_array($item)) { foreach ($item as $items){ foreach ($items as $item_){ $item_ instanceof UnitData and $inline_field[] = $item_; } } unset($data[$key]); } elseif ($item instanceof AuxTitle) { unset($data[$key]); } } $data = array_merge($inline_field, $data); $return_data = []; /** @var UnitData $datum */ foreach ($data as $datum) { $return_data[$datum->get('name')] = $datum; } return $return_data; } /** * 设置默认值 * @param array $default_data * @return Form */ public function setDefaultData(array $default_data): Form { $this->default_data = $default_data; return $this; } }