123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- <?php
- namespace Dcat\Admin\Form\Field;
- use Dcat\Admin\Admin;
- use Dcat\Admin\Form\Field;
- use Dcat\Admin\Support\Helper;
- use Illuminate\Database\Eloquent\Model;
- use Illuminate\Support\Arr;
- use Illuminate\Support\Str;
- class Select extends Field
- {
- public static $js = '@select2';
- public static $css = '@select2';
- /**
- * @var array
- */
- protected $groups = [];
- /**
- * @var array
- */
- protected $config = [];
- /**
- * Set options.
- *
- * @param array|\Closure|string $options
- *
- * @return $this|mixed
- */
- public function options($options = [])
- {
- if ($options instanceof \Closure) {
- $this->options = $options;
- return $this;
- }
- // remote options
- if (is_string($options)) {
- // reload selected
- if (class_exists($options) && in_array(Model::class, class_parents($options))) {
- return $this->model(...func_get_args());
- }
- return $this->loadRemoteOptions(...func_get_args());
- }
- $this->options = Helper::array($options);
- return $this;
- }
- /**
- * @param array $groups
- */
- /**
- * Set option groups.
- *
- * eg: $group = [
- * [
- * 'label' => 'xxxx',
- * 'options' => [
- * 1 => 'foo',
- * 2 => 'bar',
- * ...
- * ],
- * ...
- * ]
- *
- * @param array $groups
- *
- * @return $this
- */
- public function groups(array $groups)
- {
- $this->groups = $groups;
- return $this;
- }
- /**
- * Load options for other select on change.
- *
- * @param string $field
- * @param string $sourceUrl
- * @param string $idField
- * @param string $textField
- *
- * @return $this
- */
- public function load($field, $sourceUrl, $idField = 'id', $textField = 'text')
- {
- if (Str::contains($field, '.')) {
- $field = $this->formatName($field);
- $class = str_replace(['[', ']'], '_', $field);
- } else {
- $class = $field;
- }
- $sourceUrl = admin_url($sourceUrl);
- $script = <<<JS
- $(document).off('change', "{$this->getElementClassSelector()}");
- $(document).on('change', "{$this->getElementClassSelector()}", function () {
- var target = $(this).closest('.fields-group').find(".$class");
- $.ajax("$sourceUrl?q="+this.value).then(function (data) {
- target.find("option").remove();
- $(target).select2({
- data: $.map(data, function (d) {
- d.id = d.$idField;
- d.text = d.$textField;
- return d;
- })
- }).val(target.attr('data-value')).trigger('change');
- });
- });
- JS;
- Admin::script($script);
- return $this;
- }
- /**
- * Load options for other selects on change.
- *
- * @param string $fields
- * @param string $sourceUrls
- * @param string $idField
- * @param string $textField
- *
- * @return $this
- */
- public function loads($fields = [], $sourceUrls = [], $idField = 'id', $textField = 'text')
- {
- $fieldsStr = implode('.', $fields);
- $urlsStr = implode('^', array_map(function ($url) {
- return admin_url($url);
- }, (array) $sourceUrls));
- $script = <<<JS
- (function () {
- var fields = '$fieldsStr'.split('.');
- var urls = '$urlsStr'.split('^');
-
- var refreshOptions = function(url, target) {
- $.ajax(url).then(function(data) {
- target.find("option").remove();
- $(target).select2({
- data: $.map(data, function (d) {
- d.id = d.$idField;
- d.text = d.$textField;
- return d;
- })
- }).trigger('change');
- });
- };
-
- $(document).off('change', "{$this->getElementClassSelector()}");
- $(document).on('change', "{$this->getElementClassSelector()}", function () {
- var _this = this;
- var promises = [];
-
- fields.forEach(function(field, index){
- var target = $(_this).closest('.fields-group').find('.' + fields[index]);
- promises.push(refreshOptions(urls[index] + "?q="+ _this.value, target));
- });
-
- $.when(promises).then(function() {
- console.log('开始更新其它select的选择options');
- });
- });
- })()
- JS;
- Admin::script($script);
- return $this;
- }
- /**
- * Load options from current selected resource(s).
- *
- * @param string $model
- * @param string $idField
- * @param string $textField
- *
- * @return $this
- */
- public function model($model, $idField = 'id', $textField = 'name')
- {
- if (! class_exists($model)
- || ! in_array(Model::class, class_parents($model))
- ) {
- throw new \InvalidArgumentException("[$model] must be a valid model class");
- }
- $this->options = function ($value) use ($model, $idField, $textField) {
- if (empty($value)) {
- return [];
- }
- $resources = [];
- if (is_array($value)) {
- if (Arr::isAssoc($value)) {
- $resources[] = Arr::get($value, $idField);
- } else {
- $resources = array_column($value, $idField);
- }
- } else {
- $resources[] = $value;
- }
- return $model::find($resources)->pluck($textField, $idField)->toArray();
- };
- return $this;
- }
- /**
- * Load options from remote.
- *
- * @param string $url
- * @param array $parameters
- * @param array $options
- *
- * @return $this
- */
- protected function loadRemoteOptions($url, $parameters = [], $options = [])
- {
- $ajaxOptions = [
- 'url' => admin_url($url.'?'.http_build_query($parameters)),
- ];
- $configs = array_merge([
- 'allowClear' => true,
- 'placeholder' => [
- 'id' => '',
- 'text' => trans('admin.choose'),
- ],
- ], $this->config);
- $configs = json_encode($configs);
- $configs = substr($configs, 1, strlen($configs) - 2);
- $ajaxOptions = json_encode(array_merge($ajaxOptions, $options));
- $this->script = <<<JS
- $.ajax({$ajaxOptions}).done(function(data) {
- $("{$this->getElementClassSelector()}").each(function (_, select) {
- select = $(select);
- select.select2({
- data: data,
- $configs
- });
-
- var value = select.data('value') + '';
-
- if (value) {
- value = value.split(',');
- select.select2('val', value);
- }
- });
- });
- JS;
- return $this;
- }
- /**
- * Load options from ajax results.
- *
- * @param string $url
- * @param $idField
- * @param $textField
- *
- * @return $this
- */
- public function ajax($url, $idField = 'id', $textField = 'text')
- {
- $configs = array_merge([
- 'allowClear' => true,
- 'placeholder' => $this->label,
- 'minimumInputLength' => 1,
- ], $this->config);
- $configs = json_encode($configs);
- $configs = substr($configs, 1, strlen($configs) - 2);
- $url = admin_url($url);
- $this->script = <<<JS
- $("{$this->getElementClassSelector()}").select2({
- ajax: {
- url: "$url",
- dataType: 'json',
- delay: 250,
- data: function (params) {
- return {
- q: params.term,
- page: params.page
- };
- },
- processResults: function (data, params) {
- params.page = params.page || 1;
- return {
- results: $.map(data.data, function (d) {
- d.id = d.$idField;
- d.text = d.$textField;
- return d;
- }),
- pagination: {
- more: data.next_page_url
- }
- };
- },
- cache: true
- },
- $configs,
- escapeMarkup: function (markup) {
- return markup;
- }
- });
- JS;
- return $this;
- }
- /**
- * Set config for select2.
- *
- * all configurations see https://select2.org/configuration/options-api
- *
- * @param string $key
- * @param mixed $val
- *
- * @return $this
- */
- public function config($key, $val)
- {
- $this->config[$key] = $val;
- return $this;
- }
- /**
- * Disable clear button.
- *
- * @return $this
- */
- public function disableClearButton()
- {
- return $this->config('allowClear', false);
- }
- /**
- * {@inheritdoc}
- */
- public function render()
- {
- static::defineLang();
- $configs = array_merge([
- 'allowClear' => true,
- 'placeholder' => [
- 'id' => '',
- 'text' => $this->label,
- ],
- ], $this->config);
- $configs = json_encode($configs);
- if (empty($this->script)) {
- $this->script = "$(\"{$this->getElementClassSelector()}\").select2($configs);";
- }
- if ($this->options instanceof \Closure) {
- $this->options = $this->options->bindTo($this->values());
- $this->options(call_user_func($this->options, $this->value(), $this));
- }
- $this->options = array_filter($this->options, 'strlen');
- $this->addVariables([
- 'options' => $this->options,
- 'groups' => $this->groups,
- ]);
- $this->attribute('data-value', implode(',', Helper::array($this->value())));
- return parent::render();
- }
- /**
- * @return void
- */
- public static function defineLang()
- {
- $lang = trans('select2');
- if (! is_array($lang) || empty($lang)) {
- return;
- }
- $locale = config('app.locale');
- Admin::script(
- <<<JS
- (function () {
- if (! $.fn.select2) {
- return;
- }
- var e = $.fn.select2.amd;
- return e.define("select2/i18n/{$locale}", [], function () {
- return {
- errorLoading: function () {
- return "{$lang['error_loading']}"
- }, inputTooLong: function (e) {
- return "{$lang['input_too_long']}".replace(':num', e.input.length - e.maximum)
- }, inputTooShort: function (e) {
- return "{$lang['input_too_short']}".replace(':num', e.minimum - e.input.length)
- }, loadingMore: function () {
- return "{$lang['loading_more']}"
- }, maximumSelected: function (e) {
- return "{$lang['maximum_selected']}".replace(':num', e.maximum)
- }, noResults: function () {
- return "{$lang['no_results']}"
- }, searching: function () {
- return "{$lang['searching']}"
- }
- }
- }), {define: e.define, require: e.require}
- })()
- JS
- );
- }
- }
|