Form.php 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289
  1. <?php
  2. namespace fast;
  3. use ArrayAccess;
  4. /**
  5. * 表单元素生成
  6. * @class Form
  7. * @package fast
  8. * @method static string token() 生成Token
  9. * @method static string label(string $name, string $value = null, array $options = []) label标签
  10. * @method static string input($type, $name, string $value = null, array $options = []) 按类型生成文本框
  11. * @method static string text(string $name, string $value = null, array $options = []) 普通文本框
  12. * @method static string password(string $name, array $options = []) 密码文本框
  13. * @method static string hidden(string $name, string $value = null, array $options = []) 隐藏文本框
  14. * @method static string email(string $name, string $value = null, array $options = []) Email文本框
  15. * @method static string url(string $name, string $value = null, array $options = []) URL文本框
  16. * @method static string file(string $name, array $options = []) 文件上传组件
  17. * @method static string textarea(string $name, string $value = null, array $options = []) 多行文本框
  18. * @method static string editor(string $name, string $value = null, array $options = []) 富文本编辑器
  19. * @method static string select(string $name, array $list = [], string $selected = null, array $options = []) 下拉列表组件
  20. * @method static string selects(string $name, array $list = [], string $selected = null, array $options = []) 下拉列表组件(多选)
  21. * @method static string selectpicker(string $name, array $list = [], string $selected = null, array $options = []) 下拉列表组件(友好)
  22. * @method static string selectpickers(string $name, array $list = [], string $selected = null, array $options = []) 下拉列表组件(友好)(多选)
  23. * @method static string selectpage(string $name, string $value, string $url, string $field = null, string $primaryKey = null, array $options = []) 动态下拉列表组件
  24. * @method static string selectpages(string $name, string $value, string $url, string $field = null, string $primaryKey = null, array $options = []) 动态下拉列表组件(多选)
  25. * @method static string citypicker(string $name, string $value, array $options = []) 城市选择组件
  26. * @method static string switcher(string $name, string $value, array $options = []) 切换组件
  27. * @method static string datepicker(string $name, string $value, array $options = []) 日期选择组件
  28. * @method static string timepicker(string $name, string $value, array $options = []) 时间选择组件
  29. * @method static string datetimepicker(string $name, string $value, array $options = []) 日期时间选择组件
  30. * @method static string daterange(string $name, string $value, array $options = []) 日期区间组件
  31. * @method static string timerange(string $name, string $value, array $options = []) 时间区间组件
  32. * @method static string datetimerange(string $name, string $value, array $options = []) 日期时间区间组件
  33. * @method static string fieldlist(string $name, string $value, string $title = null, string $template = null, array $options = []) 字段列表组件
  34. * @method static string cxselect(string $url, array $names = [], array $values = [], array $options = []) 联动组件
  35. * @method static string selectRange(string $name, string $begin, string $end, string $selected = null, array $options = []) 选择数字区间
  36. * @method static string selectYear(string $name, string $begin, string $end, string $selected = null, array $options = []) 选择年
  37. * @method static string selectMonth(string $name, string $selected = null, array $options = [], string $format = '%m') 选择月
  38. * @method static string checkbox(string $name, string $value = '1', string $checked = null, array $options = []) 单个复选框
  39. * @method static string checkboxs(string $name, array $list = [], string $checked = null, array $options = []) 一组复选框
  40. * @method static string radio(string $name, string $value = null, string $checked = null, array $options = [])) 单个单选框
  41. * @method static string radios(string $name, array $list = [], string $checked = null, array $options = [])) 一组单选框
  42. * @method static string image(string $name = null, string $value = null, array $inputAttr = [], array $uploadAttr = [], array $chooseAttr = [], array $previewAttr = []) 上传图片组件
  43. * @method static string images(string $name = null, string $value = null, array $inputAttr = [], array $uploadAttr = [], array $chooseAttr = [], array $previewAttr = []) 上传图片组件(多图))
  44. * @method static string upload(string $name = null, string $value = null, array $inputAttr = [], array $uploadAttr = [], array $chooseAttr = [], array $previewAttr = []) 上传文件组件
  45. * @method static string uploads(string $name = null, string $value = null, array $inputAttr = [], array $uploadAttr = [], array $chooseAttr = [], array $previewAttr = []) 上传文件组件(多文件))
  46. * @method static string button(string $value = null, array $options = []) 表单button
  47. */
  48. class Form
  49. {
  50. /**
  51. * @param $name
  52. * @param $arguments
  53. * @return FormBuilder
  54. */
  55. public static function __callStatic($name, $arguments)
  56. {
  57. return call_user_func_array([FormBuilder::instance(), $name], $arguments);
  58. }
  59. }
  60. /**
  61. *
  62. * 表单元素生成
  63. * @from https://github.com/illuminate/html
  64. * @package fast
  65. */
  66. class FormBuilder
  67. {
  68. /**
  69. * Token
  70. *
  71. * @var string
  72. */
  73. protected $csrfToken = array('name' => '__token__');
  74. /**
  75. * 已创建的标签名称
  76. *
  77. * @var array
  78. */
  79. protected $labels = [];
  80. /**
  81. * 跳过的填充value值的类型
  82. *
  83. * @var array
  84. */
  85. protected $skipValueTypes = array('file', 'password', 'checkbox', 'radio');
  86. /**
  87. * 转义HTML
  88. * @var boolean
  89. */
  90. protected $escapeHtml = true;
  91. protected static $instance;
  92. /**
  93. * 获取单例
  94. * @param array $options
  95. * @return static
  96. */
  97. public static function instance($options = [])
  98. {
  99. if (is_null(self::$instance)) {
  100. self::$instance = new static($options);
  101. }
  102. return self::$instance;
  103. }
  104. /**
  105. * 设置是否转义
  106. * @param boolean $escape
  107. */
  108. public function setEscapeHtml($escape)
  109. {
  110. $this->escapeHtml = $escape;
  111. }
  112. /**
  113. * 获取转义编码后的值
  114. * @param string $value
  115. * @return string
  116. */
  117. public function escape($value)
  118. {
  119. if (!$this->escapeHtml) {
  120. return $value;
  121. }
  122. if (is_array($value)) {
  123. $value = json_encode($value, JSON_UNESCAPED_UNICODE);
  124. }
  125. return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
  126. }
  127. /**
  128. * 生成Token
  129. *
  130. * @param string $name
  131. * @param string $type
  132. * @return string
  133. */
  134. public function token($name = '__token__', $type = 'md5')
  135. {
  136. if (function_exists('token')) {
  137. return token($name, $type);
  138. }
  139. return '';
  140. }
  141. /**
  142. * 生成Label标签
  143. *
  144. * @param string $name
  145. * @param string $value
  146. * @param array $options
  147. * @return string
  148. */
  149. public function label($name, $value = null, $options = [])
  150. {
  151. $this->labels[] = $name;
  152. $options = $this->attributes($options);
  153. $value = $this->escape($this->formatLabel($name, $value));
  154. return '<label for="' . $name . '"' . $options . '>' . $value . '</label>';
  155. }
  156. /**
  157. * Format the label value.
  158. *
  159. * @param string $name
  160. * @param string|null $value
  161. * @return string
  162. */
  163. protected function formatLabel($name, $value)
  164. {
  165. return $value ?: ucwords(str_replace('_', ' ', $name));
  166. }
  167. /**
  168. * 生成文本框(按类型)
  169. *
  170. * @param string $type
  171. * @param string $name
  172. * @param string $value
  173. * @param array $options
  174. * @return string
  175. */
  176. public function input($type, $name, $value = null, $options = [])
  177. {
  178. if (!isset($options['name'])) {
  179. $options['name'] = $name;
  180. }
  181. $id = $this->getIdAttribute($name, $options);
  182. if (!in_array($type, $this->skipValueTypes)) {
  183. $value = $this->getValueAttribute($name, $value);
  184. $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' form-control') : 'form-control';
  185. }
  186. $merge = compact('type', 'value', 'id');
  187. $options = array_merge($options, $merge);
  188. return '<input' . $this->attributes($options) . '>';
  189. }
  190. /**
  191. * 生成普通文本框
  192. *
  193. * @param string $name
  194. * @param string $value
  195. * @param array $options
  196. * @return string
  197. */
  198. public function text($name, $value = null, $options = [])
  199. {
  200. return $this->input('text', $name, $value, $options);
  201. }
  202. /**
  203. * 生成密码文本框
  204. *
  205. * @param string $name
  206. * @param array $options
  207. * @return string
  208. */
  209. public function password($name, $options = [])
  210. {
  211. return $this->input('password', $name, '', $options);
  212. }
  213. /**
  214. * 生成隐藏文本框
  215. *
  216. * @param string $name
  217. * @param string $value
  218. * @param array $options
  219. * @return string
  220. */
  221. public function hidden($name, $value = null, $options = [])
  222. {
  223. return $this->input('hidden', $name, $value, $options);
  224. }
  225. /**
  226. * 生成Email文本框
  227. *
  228. * @param string $name
  229. * @param string $value
  230. * @param array $options
  231. * @return string
  232. */
  233. public function email($name, $value = null, $options = [])
  234. {
  235. return $this->input('email', $name, $value, $options);
  236. }
  237. /**
  238. * 生成URL文本框
  239. *
  240. * @param string $name
  241. * @param string $value
  242. * @param array $options
  243. * @return string
  244. */
  245. public function url($name, $value = null, $options = [])
  246. {
  247. return $this->input('url', $name, $value, $options);
  248. }
  249. /**
  250. * 生成上传文件组件
  251. *
  252. * @param string $name
  253. * @param array $options
  254. * @return string
  255. */
  256. public function file($name, $options = [])
  257. {
  258. return $this->input('file', $name, null, $options);
  259. }
  260. /**
  261. * 生成多行文本框
  262. *
  263. * @param string $name
  264. * @param string $value
  265. * @param array $options
  266. * @return string
  267. */
  268. public function textarea($name, $value = null, $options = [])
  269. {
  270. if (!isset($options['name'])) {
  271. $options['name'] = $name;
  272. }
  273. $options = $this->setTextAreaSize($options);
  274. $options['id'] = $this->getIdAttribute($name, $options);
  275. $value = (string)$this->getValueAttribute($name, $value);
  276. unset($options['size']);
  277. $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' form-control') : 'form-control';
  278. $options = $this->attributes($options);
  279. return '<textarea' . $options . '>' . $this->escape($value) . '</textarea>';
  280. }
  281. /**
  282. * 生成富文本编辑器
  283. *
  284. * @param string $name
  285. * @param string $value
  286. * @param array $options
  287. * @return string
  288. */
  289. public function editor($name, $value = null, $options = [])
  290. {
  291. $options['class'] = isset($options['class']) ? $options['class'] . ' editor' : 'editor';
  292. return $this->textarea($name, $value, $options);
  293. }
  294. /**
  295. * 设置默认的文本框行列数
  296. *
  297. * @param array $options
  298. * @return array
  299. */
  300. protected function setTextAreaSize($options)
  301. {
  302. if (isset($options['size'])) {
  303. return $this->setQuickTextAreaSize($options);
  304. }
  305. $cols = array_get($options, 'cols', 50);
  306. $rows = array_get($options, 'rows', 5);
  307. return array_merge($options, compact('cols', 'rows'));
  308. }
  309. /**
  310. * 根据size设置行数和列数
  311. *
  312. * @param array $options
  313. * @return array
  314. */
  315. protected function setQuickTextAreaSize($options)
  316. {
  317. $segments = explode('x', $options['size']);
  318. return array_merge($options, array('cols' => $segments[0], 'rows' => $segments[1]));
  319. }
  320. /**
  321. * 生成滑块
  322. *
  323. * @param string $name
  324. * @param string $min
  325. * @param string $max
  326. * @param string $step
  327. * @param string $value
  328. * @param array $options
  329. * @return string
  330. */
  331. public function slider($name, $min, $max, $step, $value = null, $options = [])
  332. {
  333. $options = array_merge($options, ['data-slider-min' => $min, 'data-slider-max' => $max, 'data-slider-step' => $step, 'data-slider-value' => $value ? $value : '']);
  334. $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' slider form-control') : 'slider form-control';
  335. return $this->input('text', $name, $value, $options);
  336. }
  337. /**
  338. * 生成下拉列表框
  339. *
  340. * @param string $name
  341. * @param array $list
  342. * @param mixed $selected
  343. * @param array $options
  344. * @return string
  345. */
  346. public function select($name, $list = [], $selected = null, $options = [])
  347. {
  348. $selected = $this->getValueAttribute($name, $selected);
  349. $options['id'] = $this->getIdAttribute($name, $options);
  350. if (!isset($options['name'])) {
  351. $options['name'] = $name;
  352. }
  353. $html = [];
  354. foreach ($list as $value => $display) {
  355. $html[] = $this->getSelectOption($display, $value, $selected);
  356. }
  357. $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' form-control') : 'form-control';
  358. $options = $this->attributes($options);
  359. $list = implode('', $html);
  360. return "<select{$options}>{$list}</select>";
  361. }
  362. /**
  363. * 下拉列表(多选)
  364. *
  365. * @param string $name
  366. * @param array $list
  367. * @param mixed $selected
  368. * @param array $options
  369. * @return string
  370. */
  371. public function selects($name, $list = [], $selected = null, $options = [])
  372. {
  373. $options[] = 'multiple';
  374. return $this->select($name, $list, $selected, $options);
  375. }
  376. /**
  377. * 下拉列表(友好)
  378. *
  379. * @param string $name
  380. * @param array $list
  381. * @param mixed $selected
  382. * @param array $options
  383. * @return string
  384. */
  385. public function selectpicker($name, $list = [], $selected = null, $options = [])
  386. {
  387. $options['class'] = isset($options['class']) ? $options['class'] . ' selectpicker' : 'selectpicker';
  388. return $this->select($name, $list, $selected, $options);
  389. }
  390. /**
  391. * 下拉列表(友好)(多选)
  392. *
  393. * @param string $name
  394. * @param array $list
  395. * @param mixed $selected
  396. * @param array $options
  397. * @return string
  398. */
  399. public function selectpickers($name, $list = [], $selected = null, $options = [])
  400. {
  401. $options[] = 'multiple';
  402. return $this->selectpicker($name, $list, $selected, $options);
  403. }
  404. /**
  405. * 生成动态下拉列表
  406. *
  407. * @param string $name 名称
  408. * @param mixed $value
  409. * @param string $url 数据源地址
  410. * @param string $field 显示的字段名称,默认为name
  411. * @param string $primaryKey 主键,数据库中保存的值,默认为id
  412. * @param array $options
  413. * @return string
  414. */
  415. public function selectpage($name, $value, $url, $field = null, $primaryKey = null, $options = [])
  416. {
  417. $options = array_merge($options, ['data-source' => $url, 'data-field' => $field ? $field : 'name', 'data-primary-key' => $primaryKey ? $primaryKey : 'id']);
  418. $options['class'] = isset($options['class']) ? $options['class'] . ' selectpage' : 'selectpage';
  419. return $this->text($name, $value, $options);
  420. }
  421. /**
  422. * 生成动态下拉列表(复选)
  423. *
  424. * @param string $name 名称
  425. * @param mixed $value
  426. * @param string $url 数据源地址
  427. * @param string $field 显示的字段名称,默认为name
  428. * @param string $primaryKey 主键,数据库中保存的值,默认为id
  429. * @param array $options
  430. * @return string
  431. */
  432. public function selectpages($name, $value, $url, $field = null, $primaryKey = null, $options = [])
  433. {
  434. $options['data-multiple'] = "true";
  435. return $this->selectpage($name, $value, $url, $field, $primaryKey, $options);
  436. }
  437. /**
  438. * 生成城市选择框
  439. *
  440. * @param string $name
  441. * @param mixed $value
  442. * @param array $options
  443. * @return string
  444. */
  445. public function citypicker($name, $value, $options = [])
  446. {
  447. $options['data-toggle'] = 'city-picker';
  448. return "<div class='control-relative'>" . $this->text($name, $value, $options) . "</div>";
  449. }
  450. /**
  451. * 生成switch组件
  452. *
  453. * @param string $name
  454. * @param mixed $value
  455. * @param array $options
  456. * @return string
  457. */
  458. public function switcher($name, $value, $options = [])
  459. {
  460. $domname = str_replace(['[', ']', '.'], '', $name);
  461. $btn = $this->hidden($name, $value, ['id' => "c-{$domname}"]);
  462. $yes = 1;
  463. $no = 0;
  464. if (isset($options['yes']) && isset($options['no'])) {
  465. $yes = $options['yes'];
  466. $no = $options['no'];
  467. }
  468. $selected = $no == $value ? "fa-flip-horizontal text-gray" : "";
  469. $disabled = (isset($options['disabled']) && $options['disabled']) || in_array('disabled', $options) ? "disabled" : '';
  470. $color = isset($options['color']) ? $options['color'] : 'success';
  471. unset($options['yes'], $options['no'], $options['color'], $options['disabled']);
  472. $attr = $this->attributes($options);
  473. $html = <<<EOD
  474. {$btn}
  475. <a href="javascript:;" data-toggle="switcher" class="btn-switcher {$disabled}" data-input-id="c-{$domname}" data-yes="{$yes}" data-no="{$no}" {$attr}><i class="fa fa-toggle-on text-{$color} {$selected} fa-2x"></i></a>
  476. EOD;
  477. return $html;
  478. }
  479. /**
  480. * 日期选择器
  481. * @param string $name
  482. * @param mixed $value
  483. * @param array $options
  484. * @return string
  485. */
  486. public function datepicker($name, $value, $options = [])
  487. {
  488. $defaults = [
  489. 'data-date-format' => "YYYY-MM-DD",
  490. ];
  491. $options = array_merge($defaults, $options);
  492. $value = is_numeric($value) ? date("Y-m-d", $value) : $value;
  493. return $this->datetimepicker($name, $value, $options);
  494. }
  495. /**
  496. * 时间选择器
  497. *
  498. * @param string $name
  499. * @param mixed $value
  500. * @param array $options
  501. * @return string
  502. */
  503. public function timepicker($name, $value, $options = [])
  504. {
  505. $defaults = [
  506. 'data-date-format' => "HH:mm:ss",
  507. ];
  508. $options = array_merge($defaults, $options);
  509. $value = is_numeric($value) ? date("H:i:s", $value) : $value;
  510. return $this->datetimepicker($name, $value, $options);
  511. }
  512. /**
  513. * 日期时间选择器
  514. *
  515. * @param string $name
  516. * @param mixed $value
  517. * @param array $options
  518. * @return string
  519. */
  520. public function datetimepicker($name, $value, $options = [])
  521. {
  522. $defaults = [
  523. 'data-date-format' => "YYYY-MM-DD HH:mm:ss",
  524. 'data-use-current' => "true",
  525. ];
  526. $value = is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
  527. $options = array_merge($defaults, $options);
  528. $options['class'] = isset($options['class']) ? $options['class'] . ' datetimepicker' : 'datetimepicker';
  529. return $this->text($name, $value, $options);
  530. }
  531. /**
  532. * 日期区间
  533. *
  534. * @param string $name
  535. * @param string $value
  536. * @param array $options
  537. * @return string
  538. */
  539. public function daterange($name, $value, $options = [])
  540. {
  541. $defaults = [
  542. 'data-locale' => [
  543. 'format' => 'YYYY-MM-DD'
  544. ]
  545. ];
  546. $options = array_merge($defaults, $options);
  547. return $this->datetimerange($name, $value, $options);
  548. }
  549. /**
  550. * 时间区间
  551. *
  552. * @param string $name
  553. * @param string $value
  554. * @param array $options
  555. * @return string
  556. */
  557. public function timerange($name, $value, $options = [])
  558. {
  559. $defaults = [
  560. 'data-locale' => [
  561. 'format' => 'HH:mm:ss'
  562. ],
  563. 'data-ranges' => [],
  564. 'data-show-custom-range-label' => "false",
  565. 'data-time-picker' => "true",
  566. ];
  567. $options = array_merge($defaults, $options);
  568. return $this->datetimerange($name, $value, $options);
  569. }
  570. /**
  571. * 日期时间区间
  572. *
  573. * @param string $name
  574. * @param string $value
  575. * @param array $options
  576. * @return string
  577. */
  578. public function datetimerange($name, $value, $options = [])
  579. {
  580. $defaults = [
  581. 'data-locale' => [
  582. 'format' => 'YYYY-MM-DD HH:mm:ss'
  583. ]
  584. ];
  585. $options = array_merge($defaults, $options);
  586. $options['class'] = isset($options['class']) ? $options['class'] . ' datetimerange' : 'datetimerange';
  587. return $this->text($name, $value, $options);
  588. }
  589. /**
  590. * 生成字段列表组件
  591. *
  592. * @param string $name
  593. * @param mixed $value
  594. * @param array $title
  595. * @param string $template
  596. * @param array $options
  597. * @return string
  598. */
  599. public function fieldlist($name, $value, $title = null, $template = null, $options = [])
  600. {
  601. $append = __('Append');
  602. $template = $template ? 'data-template="' . $template . '"' : '';
  603. $attributes = $this->attributes($options);
  604. if (is_null($title)) {
  605. $title = [__('Key'), __('Value')];
  606. }
  607. $ins = implode("\n", array_map(function ($value) {
  608. return "<ins>{$value}</ins>";
  609. }, $title));
  610. $value = is_array($value) ? json_encode($value) : $value;
  611. $html = <<<EOD
  612. <dl class="fieldlist" data-name="{$name}" {$template} {$attributes}>
  613. <dd>
  614. {$ins}
  615. </dd>
  616. <dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {$append}</a></dd>
  617. <textarea name="{$name}" class="form-control hide" cols="30" rows="5">{$value}</textarea>
  618. </dl>
  619. EOD;
  620. return $html;
  621. }
  622. /**
  623. * 生成联动下拉列表
  624. *
  625. * @param string $url 联动获取数据源的URL地址
  626. * @param array $names 联动字段名称
  627. * @param array $values 联动字段默认选中的值
  628. * @param array $options 扩展属性
  629. * @return string
  630. */
  631. public function cxselect($url, $names = [], $values = [], $options = [])
  632. {
  633. $classes = [];
  634. $cxselect = [];
  635. $attributes = $this->attributes($options);
  636. foreach ($names as $index => $value) {
  637. $level = $index + 1;
  638. $class = "cxselect-{$level}";
  639. $classes[] = $class;
  640. $selectValue = isset($values[$value]) ? $values[$value] : (isset($values[$index]) ? $values[$index] : '');
  641. $cxselect[] = <<<EOD
  642. <select class="{$class} form-control" name="{$value}" data-value="{$selectValue}" data-url="{$url}?level={$level}&name={$value}" {$attributes}></select>
  643. EOD;
  644. }
  645. $cxselect = implode("\n", $cxselect);
  646. $selects = implode(',', $classes);
  647. $html = <<<EOD
  648. <div class="form-inline" data-toggle="cxselect" data-selects="{$selects}">
  649. {$cxselect}
  650. </div>
  651. EOD;
  652. return $html;
  653. }
  654. /**
  655. * 创建一个下拉列表选择区间组件
  656. *
  657. * @param string $name
  658. * @param string $begin
  659. * @param string $end
  660. * @param string $selected
  661. * @param array $options
  662. * @return string
  663. */
  664. public function selectRange($name, $begin, $end, $selected = null, $options = [])
  665. {
  666. $range = array_combine($range = range($begin, $end), $range);
  667. return $this->select($name, $range, $selected, $options);
  668. }
  669. /**
  670. * 生成选择年组件
  671. *
  672. * @param string $name
  673. * @param string $begin
  674. * @param string $end
  675. * @param string $selected
  676. * @param array $options
  677. * @return string
  678. */
  679. public function selectYear($name, $begin, $end, $selected, $options)
  680. {
  681. return call_user_func_array(array($this, 'selectRange'), func_get_args());
  682. }
  683. /**
  684. * 生成选择月组件
  685. *
  686. * @param string $name
  687. * @param string $selected
  688. * @param array $options
  689. * @param string $format
  690. * @return string
  691. */
  692. public function selectMonth($name, $selected = null, $options = [], $format = '%m')
  693. {
  694. $months = [];
  695. foreach (range(1, 12) as $month) {
  696. $months[$month] = strftime($format, mktime(0, 0, 0, $month, 1));
  697. }
  698. return $this->select($name, $months, $selected, $options);
  699. }
  700. /**
  701. * 根据传递的值生成option
  702. *
  703. * @param string $display
  704. * @param string $value
  705. * @param string $selected
  706. * @return string
  707. */
  708. public function getSelectOption($display, $value, $selected)
  709. {
  710. if (is_array($display)) {
  711. return $this->optionGroup($display, $value, $selected);
  712. }
  713. return $this->option($display, $value, $selected);
  714. }
  715. /**
  716. * 生成optionGroup
  717. *
  718. * @param array $list
  719. * @param string $label
  720. * @param string $selected
  721. * @return string
  722. */
  723. protected function optionGroup($list, $label, $selected)
  724. {
  725. $html = [];
  726. foreach ($list as $value => $display) {
  727. $html[] = $this->option($display, $value, $selected);
  728. }
  729. return '<optgroup label="' . $this->escape($label) . '">' . implode('', $html) . '</optgroup>';
  730. }
  731. /**
  732. * 生成option选项
  733. *
  734. * @param string $display
  735. * @param string $value
  736. * @param string $selected
  737. * @return string
  738. */
  739. protected function option($display, $value, $selected)
  740. {
  741. $selected = $this->getSelectedValue($value, $selected);
  742. $options = array('value' => $this->escape($value), 'selected' => $selected);
  743. return '<option' . $this->attributes($options) . '>' . $this->escape($display) . '</option>';
  744. }
  745. /**
  746. * 检测value是否选中
  747. *
  748. * @param string $value
  749. * @param string $selected
  750. * @return string
  751. */
  752. protected function getSelectedValue($value, $selected)
  753. {
  754. if (is_array($selected)) {
  755. return in_array($value, $selected) ? 'selected' : null;
  756. }
  757. return ((string)$value == (string)$selected) ? 'selected' : null;
  758. }
  759. /**
  760. * 生成复选按钮
  761. *
  762. * @param string $name
  763. * @param mixed $value
  764. * @param bool $checked
  765. * @param array $options
  766. * @return string
  767. */
  768. public function checkbox($name, $value = 1, $checked = null, $options = [])
  769. {
  770. if ($checked) {
  771. $options['checked'] = 'checked';
  772. }
  773. return $this->input('checkbox', $name, $value, $options);
  774. }
  775. /**
  776. * 生成一组筛选框
  777. *
  778. * @param string $name
  779. * @param array $list
  780. * @param mixed $checked
  781. * @param array $options
  782. * @return string
  783. */
  784. public function checkboxs($name, $list, $checked, $options = [])
  785. {
  786. $html = [];
  787. $checked = is_null($checked) ? [] : $checked;
  788. $checked = is_array($checked) ? $checked : explode(',', $checked);
  789. foreach ($list as $k => $v) {
  790. $options['id'] = "{$name}-{$k}";
  791. $html[] = sprintf(Form::label("{$name}-{$k}", "%s " . str_replace('%', '%%', $v)), Form::checkbox("{$name}[{$k}]", $k, in_array($k, $checked), $options));
  792. }
  793. return '<div class="checkbox">' . implode(' ', $html) . '</div>';
  794. }
  795. /**
  796. * 生成单选按钮
  797. *
  798. * @param string $name
  799. * @param mixed $value
  800. * @param bool $checked
  801. * @param array $options
  802. * @return string
  803. */
  804. public function radio($name, $value = null, $checked = null, $options = [])
  805. {
  806. if (is_null($value)) {
  807. $value = $name;
  808. }
  809. if ($checked) {
  810. $options['checked'] = 'checked';
  811. }
  812. return $this->input('radio', $name, $value, $options);
  813. }
  814. /**
  815. * 生成一组单选框
  816. *
  817. * @param string $name
  818. * @param array $list
  819. * @param mixed $checked
  820. * @param array $options
  821. * @return string
  822. */
  823. public function radios($name, $list, $checked = null, $options = [])
  824. {
  825. $html = [];
  826. $checked = is_null($checked) ? key($list) : $checked;
  827. $checked = is_array($checked) ? $checked : explode(',', $checked);
  828. foreach ($list as $k => $v) {
  829. $options['id'] = "{$name}-{$k}";
  830. $html[] = sprintf(Form::label("{$name}-{$k}", "%s " . str_replace('%', '%%', $v)), Form::radio($name, $k, in_array($k, $checked), $options));
  831. }
  832. return '<div class="radio">' . implode(' ', $html) . '</div>';
  833. }
  834. /**
  835. * 生成上传图片组件(单图)
  836. *
  837. * @param string $name
  838. * @param string $value
  839. * @param array $inputAttr
  840. * @param array $uploadAttr
  841. * @param array $chooseAttr
  842. * @param array $previewAttr
  843. * @return string
  844. */
  845. public function image($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
  846. {
  847. $default = [
  848. 'data-mimetype' => 'image/gif,image/jpeg,image/png,image/jpg,image/bmp'
  849. ];
  850. $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr;
  851. $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr;
  852. return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
  853. }
  854. /**
  855. * 生成上传图片组件(多图)
  856. *
  857. * @param string $name
  858. * @param string $value
  859. * @param array $inputAttr
  860. * @param array $uploadAttr
  861. * @param array $chooseAttr
  862. * @param array $previewAttr
  863. * @return string
  864. */
  865. public function images($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
  866. {
  867. $default = [
  868. 'data-multiple' => 'true',
  869. 'data-mimetype' => 'image/gif,image/jpeg,image/png,image/jpg,image/bmp'
  870. ];
  871. $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr;
  872. $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr;
  873. return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
  874. }
  875. /**
  876. * 生成上传文件组件(单文件)
  877. *
  878. * @param string $name
  879. * @param string $value
  880. * @param array $inputAttr
  881. * @param array $uploadAttr
  882. * @param array $chooseAttr
  883. * @param array $previewAttr
  884. * @return string
  885. */
  886. public function upload($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
  887. {
  888. return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
  889. }
  890. /**
  891. * 生成上传文件组件(多文件)
  892. *
  893. * @param string $name
  894. * @param string $value
  895. * @param array $inputAttr
  896. * @param array $uploadAttr
  897. * @param array $chooseAttr
  898. * @param array $previewAttr
  899. * @return string
  900. */
  901. public function uploads($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
  902. {
  903. $default = [
  904. 'data-multiple' => 'true',
  905. ];
  906. $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr;
  907. $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr;
  908. return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
  909. }
  910. protected function uploader($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
  911. {
  912. $domname = str_replace(['[', ']', '.'], '', $name);
  913. $options = [
  914. 'id' => "faupload-{$domname}",
  915. 'class' => "btn btn-danger faupload",
  916. 'data-input-id' => "c-{$domname}",
  917. ];
  918. $upload = $uploadAttr === false ? false : true;
  919. $choose = $chooseAttr === false ? false : true;
  920. $preview = $previewAttr === false ? false : true;
  921. if ($preview) {
  922. $options['data-preview-id'] = "p-{$domname}";
  923. }
  924. $uploadBtn = $upload ? $this->button('<i class="fa fa-upload"></i> ' . __('Upload'), array_merge($options, $uploadAttr)) : '';
  925. $options = [
  926. 'id' => "fachoose-{$domname}",
  927. 'class' => "btn btn-danger fachoose",
  928. 'data-input-id' => "c-{$domname}",
  929. ];
  930. if ($preview) {
  931. $options['data-preview-id'] = "p-{$domname}";
  932. }
  933. $chooseBtn = $choose ? $this->button('<i class="fa fa-list"></i> ' . __('Choose'), array_merge($options, $chooseAttr)) : '';
  934. $previewAttrHtml = $this->attributes($previewAttr);
  935. $previewArea = $preview ? '<ul class="row list-inline faupload-preview" id="p-' . $domname . '" ' . $previewAttrHtml . '></ul>' : '';
  936. $input = $this->text($name, $value, array_merge(['size' => 50, 'id' => "c-{$domname}"], $inputAttr));
  937. $html = <<<EOD
  938. <div class="input-group">
  939. {$input}
  940. <div class="input-group-addon no-border no-padding">
  941. <span>{$uploadBtn}</span>
  942. <span>{$chooseBtn}</span>
  943. </div>
  944. <span class="msg-box n-right" for="c-{$domname}"></span>
  945. </div>
  946. {$previewArea}
  947. EOD;
  948. return $html;
  949. }
  950. /**
  951. * 生成一个按钮
  952. *
  953. * @param string $value
  954. * @param array $options
  955. * @return string
  956. */
  957. public function button($value = null, $options = [])
  958. {
  959. if (!array_key_exists('type', $options)) {
  960. $options['type'] = 'button';
  961. }
  962. return '<button' . $this->attributes($options) . '>' . $value . '</button>';
  963. }
  964. /**
  965. * 获取ID属性值
  966. *
  967. * @param string $name
  968. * @param array $attributes
  969. * @return string
  970. */
  971. public function getIdAttribute($name, $attributes)
  972. {
  973. if (array_key_exists('id', $attributes)) {
  974. return $attributes['id'];
  975. }
  976. if (in_array($name, $this->labels)) {
  977. return $name;
  978. }
  979. }
  980. /**
  981. * 获取Value属性值
  982. *
  983. * @param string $name
  984. * @param string $value
  985. * @return string
  986. */
  987. public function getValueAttribute($name, $value = null)
  988. {
  989. if (is_null($name)) {
  990. return $value;
  991. }
  992. if (!is_null($value)) {
  993. return $value;
  994. }
  995. }
  996. /**
  997. * 数组转换成一个HTML属性字符串。
  998. *
  999. * @param array $attributes
  1000. * @return string
  1001. */
  1002. public function attributes($attributes)
  1003. {
  1004. $html = [];
  1005. // 假设我们的keys 和 value 是相同的,
  1006. // 拿HTML“required”属性来说,假设是['required']数组,
  1007. // 会已 required="required" 拼接起来,而不是用数字keys去拼接
  1008. foreach ((array)$attributes as $key => $value) {
  1009. $element = $this->attributeElement($key, $value);
  1010. if (!is_null($element)) {
  1011. $html[] = $element;
  1012. }
  1013. }
  1014. return count($html) > 0 ? ' ' . implode(' ', $html) : '';
  1015. }
  1016. /**
  1017. * 拼接成一个属性。
  1018. *
  1019. * @param string $key
  1020. * @param string $value
  1021. * @return string
  1022. */
  1023. protected function attributeElement($key, $value)
  1024. {
  1025. if (is_numeric($key)) {
  1026. $key = $value;
  1027. }
  1028. if (!is_null($value)) {
  1029. if (is_array($value) || stripos($value, '"') !== false) {
  1030. $value = is_array($value) ? json_encode($value, JSON_UNESCAPED_UNICODE) : $value;
  1031. return $key . "='" . $value . "'";
  1032. } else {
  1033. return $key . '="' . $value . '"';
  1034. }
  1035. }
  1036. }
  1037. }
  1038. class Arr
  1039. {
  1040. /**
  1041. * Determine whether the given value is array accessible.
  1042. *
  1043. * @param mixed $value
  1044. * @return bool
  1045. */
  1046. public static function accessible($value)
  1047. {
  1048. return is_array($value) || $value instanceof ArrayAccess;
  1049. }
  1050. /**
  1051. * Determine if the given key exists in the provided array.
  1052. *
  1053. * @param \ArrayAccess|array $array
  1054. * @param string|int $key
  1055. * @return bool
  1056. */
  1057. public static function exists($array, $key)
  1058. {
  1059. if ($array instanceof ArrayAccess) {
  1060. return $array->offsetExists($key);
  1061. }
  1062. return array_key_exists($key, $array);
  1063. }
  1064. /**
  1065. * Get an item from an array using "dot" notation.
  1066. *
  1067. * @param \ArrayAccess|array $array
  1068. * @param string $key
  1069. * @param mixed $default
  1070. * @return mixed
  1071. */
  1072. public static function get($array, $key, $default = null)
  1073. {
  1074. if (!static::accessible($array)) {
  1075. return $default;
  1076. }
  1077. if (is_null($key)) {
  1078. return $array;
  1079. }
  1080. if (static::exists($array, $key)) {
  1081. return $array[$key];
  1082. }
  1083. foreach (explode('.', $key) as $segment) {
  1084. if (static::accessible($array) && static::exists($array, $segment)) {
  1085. $array = $array[$segment];
  1086. } else {
  1087. return $default;
  1088. }
  1089. }
  1090. return $array;
  1091. }
  1092. /**
  1093. * Get all of the given array except for a specified array of items.
  1094. *
  1095. * @param array $array
  1096. * @param array|string $keys
  1097. * @return array
  1098. */
  1099. public static function except($array, $keys)
  1100. {
  1101. static::forget($array, $keys);
  1102. return $array;
  1103. }
  1104. /**
  1105. * Remove one or many array items from a given array using "dot" notation.
  1106. *
  1107. * @param array $array
  1108. * @param array|string $keys
  1109. * @return void
  1110. */
  1111. public static function forget(&$array, $keys)
  1112. {
  1113. $original = &$array;
  1114. $keys = (array)$keys;
  1115. if (count($keys) === 0) {
  1116. return;
  1117. }
  1118. foreach ($keys as $key) {
  1119. // if the exact key exists in the top-level, remove it
  1120. if (static::exists($array, $key)) {
  1121. unset($array[$key]);
  1122. continue;
  1123. }
  1124. $parts = explode('.', $key);
  1125. // clean up before each pass
  1126. $array = &$original;
  1127. while (count($parts) > 1) {
  1128. $part = array_shift($parts);
  1129. if (isset($array[$part]) && is_array($array[$part])) {
  1130. $array = &$array[$part];
  1131. } else {
  1132. continue 2;
  1133. }
  1134. }
  1135. unset($array[array_shift($parts)]);
  1136. }
  1137. }
  1138. }
  1139. if (!function_exists('array_get')) {
  1140. /**
  1141. * Get an item from an array using "dot" notation.
  1142. *
  1143. * @param \ArrayAccess|array $array
  1144. * @param string $key
  1145. * @param mixed $default
  1146. * @return mixed
  1147. */
  1148. function array_get($array, $key, $default = null)
  1149. {
  1150. return Arr::get($array, $key, $default);
  1151. }
  1152. }
  1153. if (!function_exists('e')) {
  1154. /**
  1155. * Escape HTML special characters in a string.
  1156. *
  1157. *
  1158. * @return string
  1159. */
  1160. function e($value)
  1161. {
  1162. if (is_array($value)) {
  1163. $value = json_encode($value, JSON_UNESCAPED_UNICODE);
  1164. }
  1165. return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
  1166. }
  1167. }
  1168. if (!function_exists('array_except')) {
  1169. /**
  1170. * Get all of the given array except for a specified array of items.
  1171. *
  1172. * @param array $array
  1173. * @param array|string $keys
  1174. * @return array
  1175. */
  1176. function array_except($array, $keys)
  1177. {
  1178. return Arr::except($array, $keys);
  1179. }
  1180. }