Как реализовать такой каскадный компонент

Vue.js

Серия статей о упакованных компонентах Vue

фон компонента

Реализовать каскадный компонент на основе прототипа продукта, см. демонстрацию ниже.

级联组件

Существует множество сценариев применения, таких как: система фонового управления, туристическая система, рекламная система, маркетинговая система и т. д., которые сейчас популярны.Vue,React,AnagularТри основных фреймворка, давайте посмотрим, как их использоватьVueвыполнить

реализовать логику

Требования к функции проверки менеджера по продукту следующие:

  • Согласно выбору основной классификации на уровень подклассификации, ограничения по уровню нет (по ширине горизонтальной доски пользовательского интерфейса подходит для многоуровневого, но не так много сцен с большой глубиной )
  • Каждый уровень поддерживает весь выбор, в соответствии с дочерним может вывести выбор всех вариантов и выполнить операцию выбора над своим родителем.
  • Выбранный уровень может отображать список результатов, работать с результатами и быстро очищать результаты.
  • Количество символов в названии категории не ограничено.Название категории выбираемой области должно отображаться в центре этого элемента.Если длина слишком велика, оно будет отображаться на новой строке.
  • Структура параметров результата упрощена, каждый пункт фиксируется на одной строке, и появляется в конце, если он слишком длинный....Представитель слишком длинный, и все содержимое отображается при перемещении мыши вверх

идеи

По своей сути Vue.js состоит из «отзывчивой системы».

«Отзывчивый», идея разработки полностью отличается от Jquery.

«Реактивный» означает, что при изменении данных Vue уведомит об этом код, использующий эти данные. Например, данные используются при рендеринге представления, и при изменении данных представление автоматически обновляется.

По региональным даннымJSONВидно, что его структура

[
  {
    "value": "中国",
    "key": 1156,
    "id": 1156,
    "children": [
      {
        "value": "北京市",
        "id": 10000,
        "key": 10000,
        "children": []
      },
      {
        "value": "河北省",
        "key": 200107,
        "id": 200107,
        "children": [
          {
            "value": "石家庄",
            "key": 20010701,
            "id": 20010701
          },
          {
            "value": "唐山市",
            "key": 20010702,
            "id": 20010702,
            "children": [
               {
                  "value": "路南区",
                  "key": 2001070201,
                 "id": 2001070201,
                  "children": []
               }
            ]
          }
        ]
      }
    ]

  • Китай
    • муниципалитет
    • хх провинция
      • хх город
        • хх площадь
      • хх город
        • хх округ

Компоненты данных-кандидатов

Это циклически вложенный объект данных, и вложенность компонентов, похоже, не соответствует требованиям продукта.Если вместо иерархии используется массив, кажется, проблема вложенности данных может быть решена.

array => level 1 -> level 2 -> level 3 -> level 4

уровень 1 => текущий, дети => уровень 2 (массив) уровень 2 => текущий, дети => уровень 3 (массив) ...

каждыйlevelэто целое,

  • имеет титулtitle
  • Есть выбрать все Вычислить, все ли выбрано в данныхselect
  • подмножество совокупных данныхdata
  • в настоящее время выбралcurrent
  • отметить индекс массива текущего уровняlevel

Сначала определите пустой массив для представления компонента

const array = []

Этот компонент можно расширить, обработав данные в формате массива, так как же обрабатывать данные? Не все компоненты отображаются при инициализации компонента, пользователю должно быть разрешено выбирать текущую категорию верхнего уровня.

Получите все категории верхнего уровня и создайте первый элемент

  • название провинции =
  • данные = высшая категория
  • текущий = пустой
  • level = 1
  • select = false

array.push({title, select, data, current, level})

Добавляет элемент подмножества в этот массив при выборе категории верхнего уровня.

array.push({title, select, data, current, level}) ...

И так далее

селектор результатов

получить результат выбора компонента, Вы можете отфильтровать атрибут проверки данных, чтобы получить, Вы можете использовать вычисляемые свойства Vue, чтобы узнать результаты в любое время.

Поле выбора результата может напрямую связывать выбранный компонент расчета для создания пользовательского интерфейса результата.

Концепция компонента

  • главный компонент
  • компонент макета
  • опции

Селектор основных компонентов

Раньше отвечал за рамку компонента, левую и правую колонки, Слева область выбора, справа область результата Это базовый слой компонента, который предоставляет импортированные реквизиты унифицированным способом.数据а экспортируемый выброс事件Компоненты должны быть полностью настроены, поэтому внутренние параметры должны быть абстрагированы.

  • область выбора Более равномерно распределенное пространство, все в горизонтальном фиксированном пространстве, не может делать слишком много уровней, слишком узкое для отображения Поскольку иерархию необходимо отображать циклически, извлеченная иерархия является компонентом макета, а компонент макета состоит из标题а также滚动的选择区域сочинение
        <Row>
          <Col :span="col" v-for="(box, idx) in resource" :key="idx">
            <select-item :title="box.title">
              <select-box v-model="box.current" :data="box.data" :level="box.level" @on-child="pushChild" @on-select="selectAll" />
            </select-item>
          </Col>
        </Row>
  • Область результатов Отображается только при наличии выбора, отображается строка заголовка, область результатов может подсчитывать количество результатов, элемент выбора использует тег тега, поддерживает быстрое удаление и устанавливает вертикальную полосу прокрутки. Вы можете использовать компоненты макета, чтобы сохранить стиль в соответствии с выбранной областью,
      <Col span="7" offset="1">
        <select-item v-if="resultLen && transfer" title="已选" clear @on-clear="$emit('on-clear', {list: data})">
          <div v-for="item in result" :key="item.id" class="c-pop-tip">
            <Tag :name="item.value" closable class="c-tag-item" @on-close="handleClose">{{item.value}}</Tag>
          </div>
        </select-item>
      </Col>

элемент компоновки

Чтобы быть совместимым с областью выбора и областью результатов, количество статистических данных должно управляться переключателем. граница, управление цветом пользовательского интерфейса

Выберите все кнопки состоянияCheckBox

Компонент поля ввода поиска с кнопкой поиска

абстрактный интерфейс кнопки очистки Пользовательский интерфейс абстрактной статистики

box.png

Поле выбора (подкомпонент)

Самый важный компонент это Варианты следует разделить на две категории,

  • Во-первых, на этом уровне нет детей.
  • Во-первых, на этом уровне есть дети.

Оценка состояния ветвей может быть реализована с помощью дисплея, но с использованиемCheckBoxКомпонент, который сам имеет функцию изменения, если онv-modelBinding, его значение меняется, главное дерево будет уведомлено об этом обновлении,

Это для второго типа выше.На этом уровне нет дочернего элемента для выполнения его работы, его обновления, его родитель может вычислить полувыбранное состояние, или количество выборок может быть рассчитано у родителя, но если есть Child , здесь для ответа на все его подмножества также должно быть выбрано, если выбрано подмножество, выбор подмножества также выбран

В процессе разработки отношение изменения здесь очень сложное, и без графики оно не понятно.

  • мероприятие Изменения подмножества можно изменить, щелкнув строку, Выбранное подмножество также изменяет изменения данных

  • типографика пользовательского интерфейса

логика

двусторонняя привязка

Преимущество данных привязки v-модели: данные изменились внутренне, но также изменились на исходной стороне, пока они используются, Конечно, есть и некоторые неудобства в использовании. Какой реквизит реквизита используется для получения данных, импортированных реквизитом?value

...
props: {
  value: {
    type: Array
  }
}
...

Set нельзя изменить внутри компонента, его можно передать родительскому компоненту только через событие Какое имя метода используется для его передачи?input(Многие люди не знают о новичках)
this.$emit('input', val)

Сборка необработанных данных Выбор компонентов иерархии

Во время инициализации создайте компоненты первого уровня.title data current levelПредположим, что данные провинции и города jsoncityJsonСоздайте первый уровень данных

const data = this.cityJson.map(ret => {
  delete ret.children
  return ret
})

Когда пользователь выбирает уровеньitemЗапускается, когда действие добавляет данные уровня Когда пользователь выбирает уровеньitemЗапускается, когда действие добавляет данные уровня. Выбрать все данные на этом уровне.

выбрать все

selectAll ({level, check, cat}) {
  let index = level - 2
  let current = index > -1 ? this.resource[index].current : ''
      cat && (current = cat)
      this.$emit('on-select', {
        check,
        current,
        list: this.data
      })
}

Бросьте его в ссылку корневого компонента для обработки, в основном, чтобы зациклить атрибут проверки текущего уровня данных на true

Флажок All-Element - это щит не может выбрать, позвольте ему выбрать подкомпонент связи событий.

поиск

Существует две реализации поиска, одна из которых представляет собой обычную реализацию внешнего интерфейса, которая проверяет возможность регуляризации внешнего интерфейса и оптимизирует скорость цикла.

Другим решением является запрос результатов через фон и фильтрация данных в соответствии с результатами.Внутренние данные не могут быть использованы напрямую, потому что данные корня дерева уничтожены, и выбор не может быть рассчитан.Есть четкая функция в поиске.После галочки Выбрать текущий элемент перед поиском код такой

clearBox (level) {
      let current
      const index = level - 2
      // 还原原来所有的data
      if (index > -1) {
        current = this.resource[index].current
        this.pushChild({ level: index + 1, current })
      } else this.resource[0].data = this.data
    }

удалять

Логика очистки окна результатов относительно проста, если для атрибута проверки всех выбранных данных установлено значение false. Конечно, вы также можете использовать цикл, чтобы установить все это снова, но вам нужно использовать $set, чтобы обновить данные здесь.

<select-item
    v-if="resultLen && transfer"
    title="已选"
    clear
    @on-clear="$emit('on-clear', {list: data})">
    <div
      v-for="item in result"
      :key="item.id"
      class="c-pop-tip">
      <Tag
        :name="item.value"
        closable
        class="c-tag-item"
        @on-close="handleClose">{{item.value}}</Tag>
    </div>
  </select-item>

События являются ключом к разработке компонентов, а реакция на события обрабатывается в указанном компоненте.

код

Вставьте весь исходный код, в нем неизбежно будут какие-то ссылочные файлы, если вы не можете использовать его напрямую, пожалуйста, не распыляйте его, потому что эта статья не для ручной партии, это у вас есть определенная основа и хотите улучшить свои навыки

Селектор основных компонентов

<template>
  <div class="c-selecter">
    <Row :gutter="12">
      <Col span="16">
        <Row>
          <Col
            :span="col"
            v-for="(box, idx) in resource"
            :key="idx">
            <select-item :title="box.title">
              <select-box
                v-model="box.current"
                :data="box.data"
                :level="box.level"
                @on-child="pushChild"
                @on-select="selectAll" />
            </select-item>
          </Col>
        </Row>
      </Col>
      <Col span="7" offset="1">
        <select-item
          v-if="resultLen && transfer"
          title="已选"
          clear
          @on-clear="$emit('on-clear', {list: data})">
          <div
            v-for="item in result"
            :key="item.id"
            class="c-pop-tip">
            <Tag
              :name="item.value"
              closable
              class="c-tag-item"
              @on-close="handleClose">{{item.value}}</Tag>
          </div>
        </select-item>
      </Col>
    </Row>
  </div>
</template>
<script>
import SelectItem from './select-item.vue'
import SelectBox from './select-box.vue'
export default {
  name: 'selecter',
  components: { SelectItem, SelectBox },
  props: {
    value: {
      type: Array
    },
    title: {
      type: Array
    },
    data: {
      type: Array
    },
    transfer: {
      type: Boolean,
      default: true
    }
  },
  data () {
    return {
      resource: []
    }
  },
  computed: {
    col () {
      return 24 / this.resource.length
    },
    result () {
      return this.value
    },
    resultLen () {
      return Boolean(this.value.length)
    }
  },
  watch: {
    data (nVal) {
      if (nVal && nVal.length) this.updateResource()
      else this.resource = []
    }
  },
  methods: {
    updateResource () {
      this.resource = []
      this.resource.push({
        data: this.data,
        current: '',
        level: 1,
        title: this.title[0]
      })
    },
    handleClose (event, name) {
      this.$emit('on-delete', {list: this.data, name})
    },
    selectAll ({level, check, cat}) {
      let index = level - 2
      let current = index > -1 ? this.resource[index].current : ''
      cat && (current = cat)
      this.$emit('on-select', {
        check,
        current,
        list: this.data
      })
    },
    pushChild (params) {
      const {item, level} = params
      const len = this.resource.length
      if (level <= len - 1) {
        this.resource.splice(level, len - level)
      }
      this.resource.push({
        data: item.children,
        current: '',
        level: level + 1,
        title: this.title[level] || item.value
      })
      this.resource[level - 1].current = item.value
    }
  },
  created () {
    this.updateResource()
  }
}
</script>
<style lang="stylus" scoped>
@import "~assets/styles/mixin.styl"

.c-pop-tip
  width 100%
.c-tag-item
  width 90%
  margin 8px 8px 0
  padding 2px 6px
  display block
  font-size 14px
  height 28px
  >>>span.ivu-tag-text
    $no-wrap()
    width calc(100% - 22px)
    display inline-block
  >>>.ivu-icon-ios-close
    top -8px
</style>

элемент компоновки

<template>
  <div class="c-select-item">
    <div class="c-header">
      <span class="c-header-title">{{title}}</span>
      <span class="c-header-clear" v-if="clear" @click="$emit('on-clear')">清空全部</span>
    </div>
    <div class="c-selecter-content">
      <slot></slot>
    </div>
  </div>
</template>
<script>
export default {
  name: 'selectItem',
  props: {
    title: {
      type: String
    },
    clear: {
      type: Boolean
    }
  }
}
</script>
<style lang="stylus" scoped>
@import "~assets/styles/mixin.styl"

.c-select-item
  background-color #fff
  border solid 1px #dee4f5
  .c-header
    padding 0 12px
    height 34px
    font-size 14px
    color #333
    border-bottom solid 1px #dee4f5
    background-color #fafbfe
    .c-header-title, .c-header-clear
      height 34px
      line-height 34px
      vertical-align middle
    .c-header-clear
      color #598fe6
      float right
      cursor pointer
  .c-selecter-content
    $scroll()
    height 246px
    width 100%
    padding-bottom 8px
</style>

Поле выбора (подкомпонента)

<template>
  <div class="c-select-box">
    <div class="c-check-all">
      <div class="c-item-select c-cataract" @click="selectAll"></div>
      <Checkbox class="c-check-item" v-model="all">全选</Checkbox>
    </div>
    <div v-for="item in data" :key="item.id">
      <div v-if="item.children && item.children.length" :class="itemClasses(item)" @click="$emit('on-child', {item, level})">
        <Checkbox v-model="item.check" :indeterminate="itemIndeterminate(item)"></Checkbox>
        <span>{{item.value}}</span>
        <Icon type="ios-arrow-forward" class="c-check-arrow" size="14" color="#c1c1c1" />
        <span class="c-item-checkbox c-cataract" @click="selectItem(item)"></span>
      </div>
      <Checkbox v-else class="c-check-item" v-model="item.check">{{item.value}}</Checkbox>
    </div>
  </div>
</template>
<script>

const computeChild = (list, Vue) => {
  list.forEach(item => {
    if (item.children && item.children.length) {
      const child = item.children
      if (child.every(ret => ret.check)) Vue.$set(item, 'check', true)
      else Vue.$set(item, 'check', false)
      computeChild(child, Vue)
    }
  })
}

export default {
  name: 'selectBox',
  props: {
    value: {
      type: [String, Number]
    },
    data: {
      type: Array
    },
    level: {
      type: Number
    }
  },
  computed: {
    itemClasses () {
      return item => {
        const cls = ['c-check-item']
        item.value === this.value && cls.push('active')
        return cls
      }
    },
    all () {
      const len = this.data.filter(ret => ret.check).length
      return this.data.length === len
    }
  },
  methods: {
    selectAll () {
      this.$emit('on-select', {
        check: !this.all,
        level: this.level
      })
    },
    selectItem (item) {
      this.$emit('on-select', {
        check: !item.check,
        level: this.level,
        cat: item.value
      })
    },
    itemIndeterminate (child) {
      const hasChild = (meta) => {
        return meta.children.reduce((sum, item) => {
          let foundChilds = []
          if (item.check) sum.push(item)
          if (item.children) foundChilds = hasChild(item)
          return sum.concat(foundChilds)
        }, [])
      }
      const some = hasChild(child).length > 0
      const every = child.children && child.children.every(ret => ret.check)
      return some && !every
    }
  },
  watch: {
    data: {
      handler (nVal, oVal) {
        computeChild(nVal, this)
      },
      deep: true
    }
  },
  mounted () {
    computeChild(this.data, this)
  }
}
</script>
<style lang="stylus" scoped>
@import "~assets/styles/mixin.styl"

.c-cataract
  display block
  position absolute
  top 0
  left 0
  z-index 8
  cursor pointer
.c-check-all
  width 100%
  height 36px
  position relative
  z-index 9
  &:hover
    .c-check-item
      background-color #f8f8f8
  .c-item-select
    width 100%
    height 100%
.c-check-item
  margin 0
  padding 0 12px
  display block
  position relative
  height 36px
  line-height 36px
  &:hover
    background-color #f8f8f8
  &.active
    color #598fe6
    background-color #f8f8f8
    .c-check-arrow
      color #598fe6 !important
  .c-check-arrow
    float right
    margin-top 10px
  .c-item-checkbox
    width 36px
    height 36px
.c-select-box >>>.ivu-checkbox-indeterminate
  .ivu-checkbox-inner
    background-color #6fb3fb
    border-color #6fb3fb
</style>

Оптимизируйте опыт

  • Функция половинного выбора Категория, выбранная в подкатегории основной категории, но сокращенная до других основных категорий.Несмотря на то, что в поле результатов есть выбранные категории, подкатегория не может отображаться в поле для выбора.После запроса в сети, опыт ответа клиента не очень хорошо Хорошо, поэтому я изучил флажки半选Состояние на самом деле очень просто изменить.Пока логическое значение добавляется к вычисляемому атрибуту для отображения полувыборки, логическое значение является классификацией.dataЕсть ли выбранный элементcheck = true

  • Текст в строке слишком длинный, а отображение новой строки оптимизировано Поскольку нет ограничений на количество слов в категории, вы не можете доверять пользователю, когда вы являетесь фронтендом, и вы не можете доверять данным, возвращаемым бэкендом, и вы не можете доверять продукт.Если продукт не столкнулся с проблемой функции ограничения слов, вы все с нетерпением ждете этого.Пользователь является обычным пользователем.

    • Есть два способа решить проблему слишком длинного текста:
      • Установите фиксированную ширину в текстовой области и отображайте ее, когда она превышает длину... (Если вы хотите отобразить все целиком, вы можете только увеличить функцию отображения, наведя указатель мыши)
      • существуетitemвысота строки не используетсяline-heightпараметры, использоватьpaddingПосле выполнения верхнего и нижнего интервалов позвольте тексту переноситься автоматически (проблема в том, что центрирование значка с правой стороны увеличит количество слов, если слов слишком много).itemпункт, эстетика не такая однородная)

Сводка опыта

Многие новички во фронтенде общались с Vue в течение года или даже более двух лет, прежде чем использовать какelement ui,iview,vantБазовая библиотека пользовательского интерфейса с открытым исходным кодом, но вы можете обнаружить, что они подходят только для реализации html-кодирования со ссылкой на диаграмму прототипа, но разделение бизнес-уровня, повторное использование логики и компонентный бизнес-уровень не учат нас, как начать.

Ядром трех популярных фреймворков является быстрая разработка компонентов, и мы просто накапливаем компоненты библиотеки пользовательского интерфейса на странице компонентов маршрутизации, очевидно, что это не та эффективная разработка, которую мы хотим. Объем проекта может достигать более 100 страниц, если компоненты не разделены, объем повторяющейся работы непредсказуем, а об эффективности и речи быть не может. Итак, как вы можете использовать Vue на более глубоком уровне, как автор?На самом деле, в библиотеке элементов пользовательского интерфейса с открытым исходным кодом реализация каждого компонента на самом деле реализована очень простым методом.Если вы хотите реализовать такую ​​​​базовую библиотеку, вы найдете способ увидеть. Вы можете узнать много идей автора, посмотрев исходный код, так какие еще компоненты не могут быть реализованы?

Мастер открывает дверь, практика зависит от человека, и каждый является нашим учителем. Не знаю, согласны ли вы с...

Выше, добро пожаловать в Paizhuan ~


Добро пожаловать в мой репозиторий с открытым исходным кодом ГИТХАБ:xiejunping (Cabber) · GitHubQR-код WeChat: отсканируйте код, чтобы добавить друзей и завести друзей

微信二维码