审查视图

extend/fast/Form.php 39.7 KB
Karson authored
1 2 3 4 5 6 7
<?php

namespace fast;

use ArrayAccess;

/**
8
 * 表单元素生成
9
 * @class   Form
Karson authored
10
 * @package fast
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
 * @method string token() static 生成Token
 * @method string label(string $name, string $value = null, array $options = []) static label标签
 * @method string input($type, $name, string $value = null, array $options = []) static 按类型生成文本框
 * @method string text(string $name, string $value = null, array $options = []) static 普通文本框
 * @method string password(string $name, array $options = []) static 密码文本框
 * @method string hidden(string $name, string $value = null, array $options = []) static 隐藏文本框
 * @method string email(string $name, string $value = null, array $options = []) static Email文本框
 * @method string url(string $name, string $value = null, array $options = []) static URL文本框
 * @method string file(string $name, array $options = []) static 文件上传组件
 * @method string textarea(string $name, string $value = null, array $options = []) static 多行文本框
 * @method string editor(string $name, string $value = null, array $options = []) static 富文本编辑器
 * @method string select(string $name, array $list = [], string $selected = null, array $options = []) static 下拉列表组件
 * @method string selects(string $name, array $list = [], string $selected = null, array $options = []) static 下拉列表组件(多选)
 * @method string selectpicker(string $name, array $list = [], string $selected = null, array $options = []) static 下拉列表组件(友好)
 * @method string selectpickers(string $name, array $list = [], string $selected = null, array $options = []) static 下拉列表组件(友好)(多选)
 * @method string selectpage(string $name, string $value, string $url, string $field = null, string $primaryKey = null, array $options = []) static 动态下拉列表组件
 * @method string selectpages(string $name, string $value, string $url, string $field = null, string $primaryKey = null, array $options = []) static 动态下拉列表组件(多选)
 * @method string citypicker(string $name, string $value, array $options = []) static 城市选择组件
 * @method string switcher(string $name, string $value, array $options = []) static 切换组件
 * @method string datepicker(string $name, string $value, array $options = []) static 日期选择组件
 * @method string timepicker(string $name, string $value, array $options = []) static 时间选择组件
 * @method string datetimepicker(string $name, string $value, array $options = []) static 日期时间选择组件
 * @method string daterange(string $name, string $value, array $options = []) static 日期区间组件
 * @method string timerange(string $name, string $value, array $options = []) static 时间区间组件
 * @method string datetimerange(string $name, string $value, array $options = []) static 日期时间区间组件
 * @method string fieldlist(string $name, string $value, string $title = null, string $template = null, array $options = []) static 字段列表组件
 * @method string cxselect(string $url, array $names = [], array $values = [], array $options = []) static 联动组件
 * @method string selectRange(string $name, string $begin, string $end, string $selected = null, array $options = []) static 选择数字区间
 * @method string selectYear(string $name, string $begin, string $end, string $selected = null, array $options = []) static 选择年
 * @method string selectMonth(string $name, string $selected = null, array $options = [], string $format = '%m') static 选择月
 * @method string checkbox(string $name, string $value = '1', string $checked = null, array $options = []) static 单个复选框
 * @method string checkboxs(string $name, array $list = [], string $checked = null, array $options = []) static 一组复选框
 * @method string radio(string $name, string $value = null, string $checked = null, array $options = [])) static 单个单选框
 * @method string radios(string $name, array $list = [], string $checked = null, array $options = [])) static 一组单选框
 * @method string image(string $name = null, string $value, array $inputAttr = [], array $uploadAttr = [], array $chooseAttr = [], array $previewAttr = []) static 上传图片组件
 * @method string images(string $name = null, string $value, array $inputAttr = [], array $uploadAttr = [], array $chooseAttr = [], array $previewAttr = []) static 上传图片组件(多图))
 * @method string upload(string $name = null, string $value, array $inputAttr = [], array $uploadAttr = [], array $chooseAttr = [], array $previewAttr = []) static 上传文件组件
 * @method string uploads(string $name = null, string $value, array $inputAttr = [], array $uploadAttr = [], array $chooseAttr = [], array $previewAttr = []) static 上传文件组件(多文件))
 * @method string button(string $value = null, array $options = []) static 表单button
Karson authored
50 51 52 53
 */
class Form
{
54 55 56 57 58
    /**
     * @param $name
     * @param $arguments
     * @return FormBuilder
     */
Karson authored
59 60 61 62 63 64
    public static function __callStatic($name, $arguments)
    {
        return call_user_func_array([FormBuilder::instance(), $name], $arguments);
    }
}
65 66 67 68 69 70
/**
 *
 * 表单元素生成
 * @from https://github.com/illuminate/html
 * @package fast
 */
Karson authored
71 72 73 74
class FormBuilder
{

    /**
75
     * Token
Karson authored
76 77 78 79 80 81
     *
     * @var string
     */
    protected $csrfToken = array('name' => '__token__');

    /**
82
     * 已创建的标签名称
Karson authored
83 84 85
     *
     * @var array
     */
86
    protected $labels = [];
Karson authored
87 88

    /**
89
     * 跳过的填充value值的类型
Karson authored
90 91 92 93
     *
     * @var array
     */
    protected $skipValueTypes = array('file', 'password', 'checkbox', 'radio');
94 95

    /**
96
     * 转义HTML
97 98 99
     * @var boolean
     */
    protected $escapeHtml = true;
Karson authored
100 101
    protected static $instance;
102 103 104 105 106
    /**
     * 获取单例
     * @param array $options
     * @return static
     */
Karson authored
107 108
    public static function instance($options = [])
    {
109
        if (is_null(self::$instance)) {
Karson authored
110 111 112 113 114 115 116
            self::$instance = new static($options);
        }

        return self::$instance;
    }

    /**
117
     * 设置是否转义
118 119 120 121 122 123 124 125
     * @param boolean $escape
     */
    public function setEscapeHtml($escape)
    {
        $this->escapeHtml = $escape;
    }

    /**
126 127
     * 获取转义编码后的值
     * @param string $value
128 129 130 131
     * @return string
     */
    public function escape($value)
    {
132
        if (!$this->escapeHtml) {
133 134
            return $value;
        }
135
        if (is_array($value)) {
136 137 138 139 140 141
            $value = json_encode($value, JSON_UNESCAPED_UNICODE);
        }
        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
    }

    /**
142
     * 生成Token
Karson authored
143
     *
144 145
     * @param string $name
     * @param string $type
Karson authored
146 147 148 149
     * @return string
     */
    public function token($name = '__token__', $type = 'md5')
    {
150
        if (function_exists('token')) {
Karson authored
151 152 153 154 155 156 157
            return token($name, $type);
        }

        return '';
    }

    /**
158
     * 生成Label标签
Karson authored
159
     *
160 161 162
     * @param string $name
     * @param string $value
     * @param array  $options
Karson authored
163 164
     * @return string
     */
165
    public function label($name, $value = null, $options = [])
Karson authored
166 167 168 169
    {
        $this->labels[] = $name;

        $options = $this->attributes($options);
170
        $value = $this->escape($this->formatLabel($name, $value));
Karson authored
171 172 173 174 175 176 177

        return '<label for="' . $name . '"' . $options . '>' . $value . '</label>';
    }

    /**
     * Format the label value.
     *
178 179
     * @param string      $name
     * @param string|null $value
Karson authored
180 181 182 183
     * @return string
     */
    protected function formatLabel($name, $value)
    {
184
        return $value ?: ucwords(str_replace('_', ' ', $name));
Karson authored
185 186 187
    }

    /**
188
     * 生成文本框(按类型)
Karson authored
189
     *
190 191 192 193
     * @param string $type
     * @param string $name
     * @param string $value
     * @param array  $options
Karson authored
194 195
     * @return string
     */
196
    public function input($type, $name, $value = null, $options = [])
Karson authored
197
    {
198
        if (!isset($options['name'])) {
Karson authored
199
            $options['name'] = $name;
200
        }
Karson authored
201 202 203

        $id = $this->getIdAttribute($name, $options);
204
        if (!in_array($type, $this->skipValueTypes)) {
Karson authored
205
            $value = $this->getValueAttribute($name, $value);
206
            $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' form-control') : 'form-control';
Karson authored
207 208 209 210 211 212 213 214 215
        }

        $merge = compact('type', 'value', 'id');
        $options = array_merge($options, $merge);

        return '<input' . $this->attributes($options) . '>';
    }

    /**
216
     * 生成普通文本框
Karson authored
217
     *
218 219 220
     * @param string $name
     * @param string $value
     * @param array  $options
Karson authored
221 222
     * @return string
     */
223
    public function text($name, $value = null, $options = [])
Karson authored
224 225 226 227 228
    {
        return $this->input('text', $name, $value, $options);
    }

    /**
229
     * 生成密码文本框
Karson authored
230
     *
231 232
     * @param string $name
     * @param array  $options
Karson authored
233 234
     * @return string
     */
235
    public function password($name, $options = [])
Karson authored
236 237 238 239 240
    {
        return $this->input('password', $name, '', $options);
    }

    /**
241
     * 生成隐藏文本框
Karson authored
242
     *
243 244 245
     * @param string $name
     * @param string $value
     * @param array  $options
Karson authored
246 247
     * @return string
     */
248
    public function hidden($name, $value = null, $options = [])
Karson authored
249 250 251 252 253
    {
        return $this->input('hidden', $name, $value, $options);
    }

    /**
254
     * 生成Email文本框
Karson authored
255
     *
256 257 258
     * @param string $name
     * @param string $value
     * @param array  $options
Karson authored
259 260
     * @return string
     */
261
    public function email($name, $value = null, $options = [])
Karson authored
262 263 264 265 266
    {
        return $this->input('email', $name, $value, $options);
    }

    /**
267
     * 生成URL文本框
Karson authored
268
     *
269 270 271
     * @param string $name
     * @param string $value
     * @param array  $options
Karson authored
272 273
     * @return string
     */
274
    public function url($name, $value = null, $options = [])
Karson authored
275 276 277 278 279
    {
        return $this->input('url', $name, $value, $options);
    }

    /**
280
     * 生成上传文件组件
Karson authored
281
     *
282 283
     * @param string $name
     * @param array  $options
Karson authored
284 285
     * @return string
     */
286
    public function file($name, $options = [])
Karson authored
287 288 289 290 291
    {
        return $this->input('file', $name, null, $options);
    }

    /**
292
     * 生成多行文本框
Karson authored
293
     *
294 295 296
     * @param string $name
     * @param string $value
     * @param array  $options
Karson authored
297 298
     * @return string
     */
299
    public function textarea($name, $value = null, $options = [])
Karson authored
300
    {
301
        if (!isset($options['name'])) {
Karson authored
302
            $options['name'] = $name;
303
        }
Karson authored
304 305 306

        $options = $this->setTextAreaSize($options);
        $options['id'] = $this->getIdAttribute($name, $options);
307
        $value = (string)$this->getValueAttribute($name, $value);
Karson authored
308 309 310

        unset($options['size']);
311
        $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' form-control') : 'form-control';
Karson authored
312 313
        $options = $this->attributes($options);
314
        return '<textarea' . $options . '>' . $this->escape($value) . '</textarea>';
Karson authored
315 316 317
    }

    /**
318
     * 生成富文本编辑器
Karson authored
319
     *
320 321
     * @param string $name
     * @param string $value
322
     * @param array  $options
323 324 325 326 327 328 329 330 331 332 333
     * @return string
     */
    public function editor($name, $value = null, $options = [])
    {
        $options['class'] = isset($options['class']) ? $options['class'] . ' editor' : 'editor';
        return $this->textarea($name, $value, $options);
    }

    /**
     * 设置默认的文本框行列数
     *
334
     * @param array $options
Karson authored
335 336 337 338
     * @return array
     */
    protected function setTextAreaSize($options)
    {
339
        if (isset($options['size'])) {
Karson authored
340 341 342 343
            return $this->setQuickTextAreaSize($options);
        }

        $cols = array_get($options, 'cols', 50);
344
        $rows = array_get($options, 'rows', 5);
Karson authored
345 346 347 348 349

        return array_merge($options, compact('cols', 'rows'));
    }

    /**
350
     * 根据size设置行数和列数
Karson authored
351
     *
352
     * @param array $options
Karson authored
353 354 355 356 357 358 359 360 361
     * @return array
     */
    protected function setQuickTextAreaSize($options)
    {
        $segments = explode('x', $options['size']);
        return array_merge($options, array('cols' => $segments[0], 'rows' => $segments[1]));
    }

    /**
362
     * 生成滑块
363
     *
364 365 366 367 368
     * @param string $name
     * @param string $min
     * @param string $max
     * @param string $step
     * @param string $value
369
     * @param array  $options
370 371 372 373
     * @return string
     */
    public function slider($name, $min, $max, $step, $value = null, $options = [])
    {
374
        $options = array_merge($options, ['data-slider-min' => $min, 'data-slider-max' => $max, 'data-slider-step' => $step, 'data-slider-value' => $value ? $value : '']);
375 376 377
        $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' slider form-control') : 'slider form-control';
        return $this->input('text', $name, $value, $options);
    }
378
379
    /**
380
     * 生成下拉列表框
Karson authored
381
     *
382 383 384 385
     * @param string $name
     * @param array  $list
     * @param mixed  $selected
     * @param array  $options
Karson authored
386 387
     * @return string
     */
388
    public function select($name, $list = [], $selected = null, $options = [])
Karson authored
389 390 391 392 393
    {
        $selected = $this->getValueAttribute($name, $selected);

        $options['id'] = $this->getIdAttribute($name, $options);
394
        if (!isset($options['name'])) {
Karson authored
395
            $options['name'] = $name;
396
        }
Karson authored
397
398 399
        $html = [];
        foreach ($list as $value => $display) {
Karson authored
400 401
            $html[] = $this->getSelectOption($display, $value, $selected);
        }
402
        $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' form-control') : 'form-control';
Karson authored
403 404 405 406 407 408 409 410

        $options = $this->attributes($options);
        $list = implode('', $html);

        return "<select{$options}>{$list}</select>";
    }

    /**
411
     * 下拉列表(多选)
Karson authored
412
     *
413
     * @param string $name
414 415 416
     * @param array  $list
     * @param mixed  $selected
     * @param array  $options
Karson authored
417 418
     * @return string
     */
419
    public function selects($name, $list = [], $selected = null, $options = [])
Karson authored
420
    {
421 422
        $options[] = 'multiple';
        return $this->select($name, $list, $selected, $options);
Karson authored
423 424 425
    }

    /**
426
     * 下拉列表(友好)
Karson authored
427
     *
428
     * @param string $name
429 430 431
     * @param array  $list
     * @param mixed  $selected
     * @param array  $options
Karson authored
432 433
     * @return string
     */
434
    public function selectpicker($name, $list = [], $selected = null, $options = [])
Karson authored
435
    {
436 437
        $options['class'] = isset($options['class']) ? $options['class'] . ' selectpicker' : 'selectpicker';
        return $this->select($name, $list, $selected, $options);
Karson authored
438 439 440
    }

    /**
441
     * 下拉列表(友好)(多选)
Karson authored
442
     *
443
     * @param string $name
444 445 446
     * @param array  $list
     * @param mixed  $selected
     * @param array  $options
Karson authored
447 448
     * @return string
     */
449
    public function selectpickers($name, $list = [], $selected = null, $options = [])
Karson authored
450
    {
451 452
        $options[] = 'multiple';
        return $this->selectpicker($name, $list, $selected, $options);
Karson authored
453 454 455
    }

    /**
456
     * 生成动态下拉列表
Karson authored
457
     *
458 459 460 461
     * @param string $name       名称
     * @param mixed  $value
     * @param string $url        数据源地址
     * @param string $field      显示的字段名称,默认为name
462
     * @param string $primaryKey 主键,数据库中保存的值,默认为id
463
     * @param array  $options
Karson authored
464 465
     * @return string
     */
466
    public function selectpage($name, $value, $url, $field = null, $primaryKey = null, $options = [])
Karson authored
467
    {
468 469 470
        $options = array_merge($options, ['data-source' => $url, 'data-field' => $field ? $field : 'name', 'data-primary-key' => $primaryKey ? $primaryKey : 'id']);
        $options['class'] = isset($options['class']) ? $options['class'] . ' selectpage' : 'selectpage';
        return $this->text($name, $value, $options);
Karson authored
471 472
    }
473
Karson authored
474
    /**
475
     * 生成动态下拉列表(复选)
Karson authored
476
     *
477 478 479 480
     * @param string $name       名称
     * @param mixed  $value
     * @param string $url        数据源地址
     * @param string $field      显示的字段名称,默认为name
481
     * @param string $primaryKey 主键,数据库中保存的值,默认为id
482
     * @param array  $options
Karson authored
483 484
     * @return string
     */
485
    public function selectpages($name, $value, $url, $field = null, $primaryKey = null, $options = [])
Karson authored
486
    {
487 488
        $options['data-multiple'] = "true";
        return $this->selectpage($name, $value, $url, $field, $primaryKey, $options);
Karson authored
489 490 491
    }

    /**
492
     * 生成城市选择框
Karson authored
493
     *
494
     * @param string $name
495 496
     * @param mixed  $value
     * @param array  $options
Karson authored
497 498
     * @return string
     */
499
    public function citypicker($name, $value, $options = [])
Karson authored
500
    {
501 502
        $options['data-toggle'] = 'city-picker';
        return "<div class='control-relative'>" . $this->text($name, $value, $options) . "</div>";
Karson authored
503 504 505
    }

    /**
506
     * 生成switch组件
Karson authored
507
     *
508
     * @param string $name
509 510
     * @param mixed  $value
     * @param array  $options
Karson authored
511 512
     * @return string
     */
513
    public function switcher($name, $value, $options = [])
Karson authored
514
    {
515 516 517 518 519 520 521
        $domname = str_replace(['[', ']', '.'], '', $name);
        $btn = $this->hidden($name, $value, ['id' => "c-{$domname}"]);
        $yes = 1;
        $no = 0;
        if (isset($options['yes']) && isset($options['no'])) {
            $yes = $options['yes'];
            $no = $options['no'];
Karson authored
522
        }
523 524 525 526 527 528 529 530 531 532
        $selected = $no == $value ? "fa-flip-horizontal text-gray" : "";
        $disabled = (isset($options['disabled']) && $options['disabled']) || in_array('disabled', $options) ? "disabled" : '';
        $color = isset($options['color']) ? $options['color'] : 'success';
        unset($options['yes'], $options['no'], $options['color'], $options['disabled']);
        $attr = $this->attributes($options);
        $html = <<<EOD
{$btn}
<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>
EOD;
        return $html;
Karson authored
533 534 535
    }

    /**
536 537
     * 日期选择器
     * @param string $name
538 539
     * @param mixed  $value
     * @param array  $options
Karson authored
540 541
     * @return string
     */
542
    public function datepicker($name, $value, $options = [])
Karson authored
543
    {
544 545 546 547 548 549
        $defaults = [
            'data-date-format' => "YYYY-MM-DD",
        ];
        $options = array_merge($defaults, $options);
        $value = is_numeric($value) ? date("Y-m-d", $value) : $value;
        return $this->datetimepicker($name, $value, $options);
Karson authored
550 551 552
    }

    /**
553
     * 时间选择器
Karson authored
554
     *
555
     * @param string $name
556 557
     * @param mixed  $value
     * @param array  $options
Karson authored
558 559
     * @return string
     */
560
    public function timepicker($name, $value, $options = [])
Karson authored
561
    {
562 563 564 565 566 567
        $defaults = [
            'data-date-format' => "HH:mm:ss",
        ];
        $options = array_merge($defaults, $options);
        $value = is_numeric($value) ? date("H:i:s", $value) : $value;
        return $this->datetimepicker($name, $value, $options);
Karson authored
568 569 570
    }

    /**
571
     * 日期时间选择器
Karson authored
572
     *
573
     * @param string $name
574 575
     * @param mixed  $value
     * @param array  $options
Karson authored
576 577
     * @return string
     */
578
    public function datetimepicker($name, $value, $options = [])
Karson authored
579
    {
580 581 582 583 584 585 586 587
        $defaults = [
            'data-date-format' => "YYYY-MM-DD HH:mm:ss",
            'data-use-current' => "true",
        ];
        $value = is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
        $options = array_merge($defaults, $options);
        $options['class'] = isset($options['class']) ? $options['class'] . ' datetimepicker' : 'datetimepicker';
        return $this->text($name, $value, $options);
Karson authored
588 589 590
    }

    /**
591
     * 日期区间
Karson authored
592
     *
593 594
     * @param string $name
     * @param string $value
595
     * @param array  $options
596
     * @return string
Karson authored
597
     */
598
    public function daterange($name, $value, $options = [])
Karson authored
599
    {
600 601 602 603 604 605 606
        $defaults = [
            'data-locale' => [
                'format' => 'YYYY-MM-DD'
            ]
        ];
        $options = array_merge($defaults, $options);
        return $this->datetimerange($name, $value, $options);
Karson authored
607 608 609
    }

    /**
610
     * 时间区间
Karson authored
611
     *
612 613
     * @param string $name
     * @param string $value
614
     * @param array  $options
615
     * @return string
Karson authored
616
     */
617
    public function timerange($name, $value, $options = [])
Karson authored
618
    {
619 620 621 622 623 624 625 626 627 628
        $defaults = [
            'data-locale'                  => [
                'format' => 'HH:mm:ss'
            ],
            'data-ranges'                  => [],
            'data-show-custom-range-label' => "false",
            'data-time-picker'             => "true",
        ];
        $options = array_merge($defaults, $options);
        return $this->datetimerange($name, $value, $options);
Karson authored
629 630 631
    }

    /**
632
     * 日期时间区间
Karson authored
633
     *
634 635
     * @param string $name
     * @param string $value
636
     * @param array  $options
637
     * @return string
Karson authored
638
     */
639
    public function datetimerange($name, $value, $options = [])
Karson authored
640
    {
641 642 643 644 645 646 647 648
        $defaults = [
            'data-locale' => [
                'format' => 'YYYY-MM-DD HH:mm:ss'
            ]
        ];
        $options = array_merge($defaults, $options);
        $options['class'] = isset($options['class']) ? $options['class'] . ' datetimerange' : 'datetimerange';
        return $this->text($name, $value, $options);
Karson authored
649 650 651
    }

    /**
652
     * 生成字段列表组件
Karson authored
653
     *
654
     * @param string $name
655 656
     * @param mixed  $value
     * @param array  $title
657
     * @param string $template
658
     * @param array  $options
659
     * @return string
Karson authored
660
     */
661
    public function fieldlist($name, $value, $title = null, $template = null, $options = [])
Karson authored
662
    {
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
        $append = __('Append');
        $template = $template ? 'data-template="' . $template . '"' : '';
        $attributes = $this->attributes($options);
        if (is_null($title)) {
            $title = [__('Key'), __('Value')];
        }
        $ins = implode("\n", array_map(function ($value) {
            return "<ins>{$value}</ins>";
        }, $title));
        $value = is_array($value) ? json_encode($value) : $value;
        $html = <<<EOD
<dl class="fieldlist" data-name="{$name}" {$template} {$attributes}>
    <dd>
        {$ins}
    </dd>
    <dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {$append}</a></dd>
    <textarea name="{$name}" class="form-control hide" cols="30" rows="5">{$value}</textarea>
</dl>
EOD;
        return $html;
Karson authored
683 684 685
    }

    /**
686
     * 生成联动下拉列表
Karson authored
687
     *
688 689 690 691
     * @param string $url     联动获取数据源的URL地址
     * @param array  $names   联动字段名称
     * @param array  $values  联动字段默认选中的值
     * @param array  $options 扩展属性
Karson authored
692 693
     * @return string
     */
694
    public function cxselect($url, $names = [], $values = [], $options = [])
Karson authored
695
    {
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
        $classes = [];
        $cxselect = [];
        $attributes = $this->attributes($options);
        foreach ($names as $index => $value) {
            $level = $index + 1;
            $class = "cxselect-{$level}";
            $classes[] = $class;
            $selectValue = isset($values[$value]) ? $values[$value] : (isset($values[$index]) ? $values[$index] : '');

            $cxselect[] = <<<EOD
<select class="{$class} form-control" name="{$value}" data-value="{$selectValue}" data-url="{$url}?level={$level}&name={$value}" {$attributes}></select>
EOD;
        }
        $cxselect = implode("\n", $cxselect);
        $selects = implode(',', $classes);
        $html = <<<EOD
<div class="form-inline" data-toggle="cxselect" data-selects="{$selects}">
{$cxselect}
</div>
EOD;
        return $html;
Karson authored
717 718 719
    }

    /**
720
     * 创建一个下拉列表选择区间组件
Karson authored
721
     *
722 723 724 725 726
     * @param string $name
     * @param string $begin
     * @param string $end
     * @param string $selected
     * @param array  $options
Karson authored
727 728
     * @return string
     */
729
    public function selectRange($name, $begin, $end, $selected = null, $options = [])
Karson authored
730
    {
731 732
        $range = array_combine($range = range($begin, $end), $range);
        return $this->select($name, $range, $selected, $options);
Karson authored
733 734 735
    }

    /**
736
     * 生成选择年组件
Karson authored
737
     *
738 739 740 741 742
     * @param string $name
     * @param string $begin
     * @param string $end
     * @param string $selected
     * @param array  $options
Karson authored
743 744
     * @return string
     */
745
    public function selectYear($name, $begin, $end, $selected, $options)
Karson authored
746
    {
747
        return call_user_func_array(array($this, 'selectRange'), func_get_args());
Karson authored
748 749 750
    }

    /**
751
     * 生成选择月组件
Karson authored
752
     *
753 754 755 756
     * @param string $name
     * @param string $selected
     * @param array  $options
     * @param string $format
Karson authored
757 758
     * @return string
     */
759
    public function selectMonth($name, $selected = null, $options = [], $format = '%m')
Karson authored
760
    {
761 762 763 764
        $months = [];

        foreach (range(1, 12) as $month) {
            $months[$month] = strftime($format, mktime(0, 0, 0, $month, 1));
Karson authored
765 766
        }
767
        return $this->select($name, $months, $selected, $options);
Karson authored
768 769 770
    }

    /**
771
     * 根据传递的值生成option
Karson authored
772
     *
773 774 775
     * @param string $display
     * @param string $value
     * @param string $selected
Karson authored
776 777
     * @return string
     */
778
    public function getSelectOption($display, $value, $selected)
Karson authored
779
    {
780 781 782
        if (is_array($display)) {
            return $this->optionGroup($display, $value, $selected);
        }
Karson authored
783
784
        return $this->option($display, $value, $selected);
Karson authored
785 786 787
    }

    /**
788
     * 生成optionGroup
Karson authored
789
     *
790 791 792
     * @param array  $list
     * @param string $label
     * @param string $selected
Karson authored
793 794
     * @return string
     */
795 796 797 798 799 800 801 802 803 804 805 806 807
    protected function optionGroup($list, $label, $selected)
    {
        $html = [];

        foreach ($list as $value => $display) {
            $html[] = $this->option($display, $value, $selected);
        }

        return '<optgroup label="' . $this->escape($label) . '">' . implode('', $html) . '</optgroup>';
    }

    /**
     * 生成option选项
Karson authored
808
     *
809 810 811
     * @param string $display
     * @param string $value
     * @param string $selected
Karson authored
812 813
     * @return string
     */
814 815 816
    protected function option($display, $value, $selected)
    {
        $selected = $this->getSelectedValue($value, $selected);
Karson authored
817
818 819 820 821
        $options = array('value' => $this->escape($value), 'selected' => $selected);

        return '<option' . $this->attributes($options) . '>' . $this->escape($display) . '</option>';
    }
Karson authored
822 823

    /**
824
     * 检测value是否选中
Karson authored
825
     *
826 827
     * @param string $value
     * @param string $selected
Karson authored
828 829
     * @return string
     */
830 831 832 833 834
    protected function getSelectedValue($value, $selected)
    {
        if (is_array($selected)) {
            return in_array($value, $selected) ? 'selected' : null;
        }
Karson authored
835
836 837
        return ((string)$value == (string)$selected) ? 'selected' : null;
    }
Karson authored
838 839

    /**
840
     * 生成复选按钮
Karson authored
841
     *
842 843 844 845
     * @param string $name
     * @param mixed  $value
     * @param bool   $checked
     * @param array  $options
Karson authored
846 847
     * @return string
     */
848 849
    public function checkbox($name, $value = 1, $checked = null, $options = [])
    {
850
        if ($checked) {
851
            $options['checked'] = 'checked';
852
        }
Karson authored
853
854 855
        return $this->input('checkbox', $name, $value, $options);
    }
Karson authored
856 857

    /**
858
     * 生成一组筛选框
Karson authored
859
     *
860
     * @param string $name
861 862 863
     * @param array  $list
     * @param mixed  $checked
     * @param array  $options
Karson authored
864 865
     * @return string
     */
866
    public function checkboxs($name, $list, $checked, $options = [])
Karson authored
867
    {
868 869 870 871 872
        $html = [];
        $checked = is_null($checked) ? [] : $checked;
        $checked = is_array($checked) ? $checked : explode(',', $checked);
        foreach ($list as $k => $v) {
            $options['id'] = "{$name}-{$k}";
F4NNIU authored
873
            $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::checkbox("{$name}[{$k}]", $k, in_array($k, $checked), $options));
Karson authored
874
        }
875 876
        return '<div class="checkbox">' . implode(' ', $html) . '</div>';
    }
Karson authored
877
878 879 880
    /**
     * 生成单选按钮
     *
881 882 883 884
     * @param string $name
     * @param mixed  $value
     * @param bool   $checked
     * @param array  $options
885 886 887 888
     * @return string
     */
    public function radio($name, $value = null, $checked = null, $options = [])
    {
889
        if (is_null($value)) {
890
            $value = $name;
891
        }
Karson authored
892
893
        if ($checked) {
894
            $options['checked'] = 'checked';
895
        }
Karson authored
896
897
        return $this->input('radio', $name, $value, $options);
Karson authored
898 899 900
    }

    /**
901
     * 生成一组单选框
Karson authored
902
     *
903
     * @param string $name
904 905 906
     * @param array  $list
     * @param mixed  $checked
     * @param array  $options
Karson authored
907 908
     * @return string
     */
909
    public function radios($name, $list, $checked = null, $options = [])
Karson authored
910
    {
911 912 913 914 915 916
        $html = [];
        $checked = is_null($checked) ? key($list) : $checked;
        $checked = is_array($checked) ? $checked : explode(',', $checked);
        foreach ($list as $k => $v) {
            $options['id'] = "{$name}-{$k}";
            $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::radio($name, $k, in_array($k, $checked), $options));
Karson authored
917
        }
918
        return '<div class="radio">' . implode(' ', $html) . '</div>';
Karson authored
919 920 921
    }

    /**
922
     * 生成上传图片组件(单图)
Karson authored
923
     *
924 925
     * @param string $name
     * @param string $value
926 927 928 929
     * @param array  $inputAttr
     * @param array  $uploadAttr
     * @param array  $chooseAttr
     * @param array  $previewAttr
Karson authored
930 931
     * @return string
     */
932
    public function image($name = null, $value, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
Karson authored
933
    {
934 935 936
        $default = [
            'data-mimetype' => 'image/gif,image/jpeg,image/png,image/jpg,image/bmp'
        ];
937
        $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr;
938 939 940
        $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr;
        return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
    }
Karson authored
941
942 943 944 945 946
    /**
     * 生成上传图片组件(多图)
     *
     * @param string $name
     * @param string $value
947 948 949 950
     * @param array  $inputAttr
     * @param array  $uploadAttr
     * @param array  $chooseAttr
     * @param array  $previewAttr
951 952 953 954 955 956 957 958
     * @return string
     */
    public function images($name = null, $value, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
    {
        $default = [
            'data-multiple' => 'true',
            'data-mimetype' => 'image/gif,image/jpeg,image/png,image/jpg,image/bmp'
        ];
959
        $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr;
960 961 962
        $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr;
        return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
    }
Karson authored
963
964 965 966 967 968
    /**
     * 生成上传文件组件(单文件)
     *
     * @param string $name
     * @param string $value
969 970 971 972
     * @param array  $inputAttr
     * @param array  $uploadAttr
     * @param array  $chooseAttr
     * @param array  $previewAttr
973 974 975 976 977
     * @return string
     */
    public function upload($name = null, $value, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
    {
        return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
Karson authored
978 979 980
    }

    /**
981
     * 生成上传文件组件(多文件)
Karson authored
982
     *
983 984
     * @param string $name
     * @param string $value
985 986 987 988
     * @param array  $inputAttr
     * @param array  $uploadAttr
     * @param array  $chooseAttr
     * @param array  $previewAttr
Karson authored
989 990
     * @return string
     */
991
    public function uploads($name = null, $value, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
Karson authored
992
    {
993 994 995
        $default = [
            'data-multiple' => 'true',
        ];
996
        $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr;
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
        $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr;
        return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr);
    }

    protected function uploader($name = null, $value, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = [])
    {
        $domname = str_replace(['[', ']', '.'], '', $name);
        $options = [
            'id'            => "plupload-{$domname}",
            'class'         => "btn btn-danger plupload",
            'data-input-id' => "c-{$domname}",
        ];
        $upload = $uploadAttr === false ? false : true;
        $choose = $chooseAttr === false ? false : true;
        $preview = $previewAttr === false ? false : true;
        if ($preview) {
            $options['data-preview-id'] = "p-{$domname}";
Karson authored
1014
        }
1015 1016 1017 1018 1019 1020 1021 1022
        $uploadBtn = $upload ? $this->button('<i class="fa fa-upload"></i> ' . __('Upload'), array_merge($options, $uploadAttr)) : '';
        $options = [
            'id'            => "fachoose-{$domname}",
            'class'         => "btn btn-danger fachoose",
            'data-input-id' => "c-{$domname}",
        ];
        if ($preview) {
            $options['data-preview-id'] = "p-{$domname}";
Karson authored
1023
        }
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
        $chooseBtn = $choose ? $this->button('<i class="fa fa-list"></i> ' . __('Choose'), array_merge($options, $chooseAttr)) : '';
        $previewAttrHtml = $this->attributes($previewAttr);
        $previewArea = $preview ? '<ul class="row list-inline plupload-preview" id="p-' . $domname . '" ' . $previewAttrHtml . '></ul>' : '';
        $input = $this->text($name, $value, array_merge(['size' => 50, 'id' => "c-{$domname}"], $inputAttr));
        $html = <<<EOD
<div class="input-group">
                {$input}
                <div class="input-group-addon no-border no-padding">
                    <span>{$uploadBtn}</span>                  
                    <span>{$chooseBtn}</span>
                </div>
                <span class="msg-box n-right" for="c-{$domname}"></span>
            </div>
            {$previewArea}
EOD;
        return $html;
Karson authored
1040 1041 1042
    }

    /**
1043
     * 生成一个按钮
Karson authored
1044
     *
1045 1046
     * @param string $value
     * @param array  $options
Karson authored
1047 1048
     * @return string
     */
1049
    public function button($value = null, $options = [])
Karson authored
1050
    {
1051 1052
        if (!array_key_exists('type', $options)) {
            $options['type'] = 'button';
Karson authored
1053
        }
1054 1055

        return '<button' . $this->attributes($options) . '>' . $value . '</button>';
Karson authored
1056 1057 1058
    }

    /**
1059
     * 获取ID属性值
Karson authored
1060
     *
1061 1062
     * @param string $name
     * @param array  $attributes
1063
     * @return string
Karson authored
1064
     */
1065
    public function getIdAttribute($name, $attributes)
Karson authored
1066
    {
1067 1068 1069 1070 1071 1072 1073
        if (array_key_exists('id', $attributes)) {
            return $attributes['id'];
        }

        if (in_array($name, $this->labels)) {
            return $name;
        }
Karson authored
1074 1075 1076
    }

    /**
1077
     * 获取Value属性值
Karson authored
1078
     *
1079 1080
     * @param string $name
     * @param string $value
Karson authored
1081 1082
     * @return string
     */
1083
    public function getValueAttribute($name, $value = null)
Karson authored
1084
    {
1085
        if (is_null($name)) {
1086
            return $value;
1087
        }
1088
1089
        if (!is_null($value)) {
1090
            return $value;
1091
        }
Karson authored
1092 1093 1094 1095 1096
    }

    /**
     * 数组转换成一个HTML属性字符串。
     *
1097
     * @param array $attributes
Karson authored
1098 1099 1100 1101
     * @return string
     */
    public function attributes($attributes)
    {
1102
        $html = [];
Karson authored
1103 1104 1105
        // 假设我们的keys 和 value 是相同的,
        // 拿HTML“required”属性来说,假设是['required']数组,
        // 会已 required="required" 拼接起来,而不是用数字keys去拼接
1106
        foreach ((array)$attributes as $key => $value) {
Karson authored
1107
            $element = $this->attributeElement($key, $value);
1108
            if (!is_null($element)) {
Karson authored
1109
                $html[] = $element;
1110
            }
Karson authored
1111 1112 1113 1114 1115 1116 1117
        }
        return count($html) > 0 ? ' ' . implode(' ', $html) : '';
    }

    /**
     * 拼接成一个属性。
     *
1118 1119
     * @param string $key
     * @param string $value
Karson authored
1120 1121 1122 1123
     * @return string
     */
    protected function attributeElement($key, $value)
    {
1124
        if (is_numeric($key)) {
Karson authored
1125
            $key = $value;
1126
        }
1127 1128 1129 1130 1131 1132 1133 1134
        if (!is_null($value)) {
            if (is_array($value) || stripos($value, '"') !== false) {
                $value = is_array($value) ? json_encode($value, JSON_UNESCAPED_UNICODE) : $value;
                return $key . "='" . $value . "'";
            } else {
                return $key . '="' . $value . '"';
            }
        }
Karson authored
1135 1136 1137 1138 1139 1140 1141 1142 1143
    }
}

class Arr
{

    /**
     * Determine whether the given value is array accessible.
     *
1144
     * @param mixed $value
Karson authored
1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
     * @return bool
     */
    public static function accessible($value)
    {
        return is_array($value) || $value instanceof ArrayAccess;
    }

    /**
     * Determine if the given key exists in the provided array.
     *
1155 1156
     * @param \ArrayAccess|array $array
     * @param string|int         $key
Karson authored
1157 1158 1159 1160
     * @return bool
     */
    public static function exists($array, $key)
    {
1161
        if ($array instanceof ArrayAccess) {
Karson authored
1162 1163 1164 1165 1166 1167 1168 1169
            return $array->offsetExists($key);
        }
        return array_key_exists($key, $array);
    }

    /**
     * Get an item from an array using "dot" notation.
     *
1170 1171 1172
     * @param \ArrayAccess|array $array
     * @param string             $key
     * @param mixed              $default
Karson authored
1173 1174 1175 1176
     * @return mixed
     */
    public static function get($array, $key, $default = null)
    {
1177
        if (!static::accessible($array)) {
Karson authored
1178 1179
            return $default;
        }
1180
        if (is_null($key)) {
Karson authored
1181 1182
            return $array;
        }
1183
        if (static::exists($array, $key)) {
Karson authored
1184 1185
            return $array[$key];
        }
1186 1187
        foreach (explode('.', $key) as $segment) {
            if (static::accessible($array) && static::exists($array, $segment)) {
Karson authored
1188
                $array = $array[$segment];
1189
            } else {
Karson authored
1190 1191 1192 1193 1194 1195 1196 1197 1198
                return $default;
            }
        }
        return $array;
    }

    /**
     * Get all of the given array except for a specified array of items.
     *
1199 1200
     * @param array        $array
     * @param array|string $keys
Karson authored
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
     * @return array
     */
    public static function except($array, $keys)
    {
        static::forget($array, $keys);
        return $array;
    }

    /**
     * Remove one or many array items from a given array using "dot" notation.
     *
1212 1213
     * @param array        $array
     * @param array|string $keys
Karson authored
1214 1215 1216 1217 1218
     * @return void
     */
    public static function forget(&$array, $keys)
    {
        $original = &$array;
1219 1220
        $keys = (array)$keys;
        if (count($keys) === 0) {
Karson authored
1221 1222
            return;
        }
1223
        foreach ($keys as $key) {
Karson authored
1224
            // if the exact key exists in the top-level, remove it
1225
            if (static::exists($array, $key)) {
Karson authored
1226 1227 1228 1229 1230 1231
                unset($array[$key]);
                continue;
            }
            $parts = explode('.', $key);
            // clean up before each pass
            $array = &$original;
1232
            while (count($parts) > 1) {
Karson authored
1233
                $part = array_shift($parts);
1234
                if (isset($array[$part]) && is_array($array[$part])) {
Karson authored
1235
                    $array = &$array[$part];
1236
                } else {
Karson authored
1237 1238 1239 1240 1241 1242 1243 1244
                    continue 2;
                }
            }
            unset($array[array_shift($parts)]);
        }
    }
}
1245
if (!function_exists('array_get')) {
Karson authored
1246 1247 1248 1249

    /**
     * Get an item from an array using "dot" notation.
     *
1250 1251 1252
     * @param \ArrayAccess|array $array
     * @param string             $key
     * @param mixed              $default
Karson authored
1253 1254 1255 1256 1257 1258 1259
     * @return mixed
     */
    function array_get($array, $key, $default = null)
    {
        return Arr::get($array, $key, $default);
    }
}
1260
if (!function_exists('e')) {
Karson authored
1261 1262 1263 1264 1265 1266 1267 1268 1269

    /**
     * Escape HTML special characters in a string.
     *
     *
     * @return string
     */
    function e($value)
    {
1270
        if (is_array($value)) {
Karson authored
1271 1272 1273 1274 1275
            $value = json_encode($value, JSON_UNESCAPED_UNICODE);
        }
        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
    }
}
1276
if (!function_exists('array_except')) {
Karson authored
1277 1278 1279 1280

    /**
     * Get all of the given array except for a specified array of items.
     *
1281 1282
     * @param array        $array
     * @param array|string $keys
Karson authored
1283 1284 1285 1286 1287 1288
     * @return array
     */
    function array_except($array, $keys)
    {
        return Arr::except($array, $keys);
    }
1289
}