Просмотр исходного кода

feat: 添加行业选择行组件

cuiyalong 4 лет назад
Родитель
Сommit
8e7bfd8fbd

+ 29 - 288
src/components/selector/IndustrySelector.vue

@@ -1,300 +1,52 @@
 <template>
   <selector-card
-    class="industry-selector card"
+    class="industry-selector"
+    :cardType="selectorType"
     @onConfirm="onConfirm"
     @onCancel="onCancel"
   >
-    <div slot="header">选择行业分类</div>
-    <div class="selector-content" v-loading="loading">
-      <div class="search-container">
-        <el-input v-model.trim="searchContent" placeholder="搜索" prefix-icon="el-icon-search"></el-input>
-      </div>
-      <div class="select-list scrollbar" ref="selectList">
-        <div
-          v-for="(item, index) in industryListMap"
-          :key="index"
-          class="list-item-container"
-        >
-          <div class="list-item-header">
-            <button
-              class="j-button-item button-level-1"
-              :class="{
-                active: item.selected,
-                [item.id]: true
-              }"
-              @click="changeIndustryState(item)"
-            >{{ item.name }}</button>
-          </div>
-          <div class="list-item-main">
-            <button
-              v-for="(iitem, iindex) in item.children" :key="999-iindex"
-              class="j-button-item button-level-2"
-              :class="{
-                active: iitem.selected,
-                [iitem.id]: true
-              }"
-              @click="changeIndustryState(iitem)"
-              >{{ iitem.name }}</button>
-          </div>
-        </div>
-      </div>
-    </div>
+    <div slot="header" v-if="selectorType === 'card'" key="header">选择行业分类</div>
+    <div slot="header" class="s-header" v-if="selectorType === 'line'" key="header">行业分类:</div>
+    <IndustrySelectorContent
+      ref="industrySelectorContent"
+      @onChange="onChange"
+      :selectorType="selectorType"
+      :initIndustry="initIndustry"
+    />
   </selector-card>
 </template>
 
 <script>
-import { Input } from 'element-ui'
 import SelectorCard from '@/components/selector/SelectorCard.vue'
-import { industryListMapExp } from '@/assets/js/selector.js'
-import { debounce, getRandomString } from '@/utils/'
+import IndustrySelectorContent from '@/components/selector/IndustrySelectorContent.vue'
 export default {
-  name: 'industry-selector-card',
+  name: 'industry-selector',
   components: {
-    [Input.name]: Input,
-    SelectorCard
+    SelectorCard,
+    IndustrySelectorContent
   },
   props: {
+    selectorType: {
+      type: String,
+      default: 'card'
+    },
     initIndustry: {
       type: Object,
       default () {
-        return {
-          // '建筑工程': [
-          //     '勘察设计',
-          //     '工程施工',
-          //     '监理咨询',
-          //     '材料设备',
-          // ],
-          // '水利水电': [
-          //     '水利工程',
-          //     '发电工程',
-          //     '航运工程',
-          //     '其他工程',
-          // ]
-        }
+        return {}
       }
     }
   },
   data () {
-    return {
-      loading: false,
-      searchContent: '',
-      // 原始数据
-      industryListMapExp,
-      // 页面中循环的数据
-      industryListMap: [],
-      industryExp: {
-        name: '全部',
-        selected: false,
-        level: 0,
-        children: [],
-        id: ''
-      }
-    }
-  },
-  watch: {
-    initIndustry (newVal, oldVal) {
-      this.setIndustryState(newVal)
-    },
-    searchContent: debounce(function (newVal, oldVal) {
-      const search = newVal
-      const id = this.getIndexWithString(search)
-      if (id) {
-        this.$nextTick(() => {
-          const wrapper = document.querySelector('.industry-selector.card')
-          this.$refs.selectList.scrollTop = wrapper.querySelector(`.${id}`).offsetTop
-        })
-      }
-    }, 300)
-  },
-  created () {
-    this.initIndustryMap()
-    this.setIndustryState(this.initIndustry)
+    return {}
   },
+  created () {},
   methods: {
-    changeLoadingState (s) {
-      this.loading = s
-    },
-    // 整理数据列表
-    initIndustryMap () {
-      const industryListMap = []
-
-      // 全部
-      const all = JSON.parse(JSON.stringify(this.industryExp))
-      all.selected = true
-      all.id = `lv0-${getRandomString(8).toLowerCase()}`
-      industryListMap.push(all)
-
-      for (const key in this.industryListMapExp) {
-        const level1 = JSON.parse(JSON.stringify(this.industryExp))
-        level1.name = key
-        level1.level = 1
-        level1.id = `lv1-${getRandomString(8).toLowerCase()}`
-
-        const level2Arr = []
-        this.industryListMapExp[key].forEach(item => {
-          const level2 = JSON.parse(JSON.stringify(this.industryExp))
-          level2.name = item
-          level2.level = 2
-          level2.id = `lv2-${getRandomString(8).toLowerCase()}`
-          level2Arr.push(level2)
-        })
-
-        level1.children = level2Arr
-        industryListMap.push(level1)
-      }
-
-      this.industryListMap = industryListMap
-    },
-    // 按钮点击事件
-    changeIndustryState (item) {
-      // 循环所有数据,判断并改变状态
-      switch (item.level) {
-        case 0: {
-          this.setIndustryState()
-          break
-        }
-        case 1: {
-          item.selected = !item.selected
-
-          this.industryListMap[0].selected = false
-          // 二级子按钮状态跟随一级按钮
-          item.children.forEach(level2 => {
-            level2.selected = item.selected
-          })
-          break
-        }
-        case 2: {
-          item.selected = !item.selected
-
-          // 找到当前点击的父级元素
-          this.industryListMap.forEach(level1 => {
-            const selectedStateArr = []
-            if (item.level !== 0) {
-              level1.children.forEach(level2 => {
-                selectedStateArr.push(level2.selected)
-              })
-              if (selectedStateArr.indexOf(false) === -1) {
-                level1.selected = true
-              } else {
-                level1.selected = false
-              }
-            }
-          })
-
-          this.industryListMap[0].selected = false
-          break
-        }
-        default: {
-          console.log('未知level')
-        }
-      }
-
-      if (item.level !== 0) {
-        const allSelected = this.checkAllSelectedState()
-        if (allSelected.allSelected || allSelected.allNotSelected) {
-          this.setIndustryState()
-        }
-      }
-    },
-    // 检查是否全部选中了/全部不选中
-    checkAllSelectedState () {
-      // 一级标签选中状态(如果一级标签全部被选中,则说明,全部按钮被选中)
-      const level1StateArr = []
-      // 所有标签选中状态
-      const allSelectedArr = []
-
-      this.industryListMap.forEach(level1 => {
-        if (level1.level !== 0) {
-          level1StateArr.push(level1.selected)
-          allSelectedArr.push(level1.selected)
-          level1.children.forEach(level2 => {
-            allSelectedArr.push(level2.selected)
-          })
-        }
-      })
-
-      return {
-        // 找不到false,就说明全部被选中
-        allSelected: level1StateArr.indexOf(false) === -1,
-        // 找不到true,就说明没有一个被选中
-        allNotSelected: allSelectedArr.indexOf(true) === -1
-      }
-    },
-    /**
-     * 初始化页面选中状态
-     * @param { Array | undefined } data 要恢复的数据
-     */
     setIndustryState (data) {
-      // 设置全部按钮
-      if (!data || Object.keys(data).length === 0) {
-        // 其他全部设置不选中,全部按钮设置选中
-        this.industryListMap.forEach(item => {
-          item.selected = false
-          item.children.forEach(iitem => {
-            iitem.selected = false
-          })
-        })
-
-        this.industryListMap[0].selected = true
-      } else {
-        this.setIndustryState()
-        this.industryListMap[0].selected = false
-
-        this.industryListMap.forEach(item => {
-          if (data[item.name]) {
-            // 如果恢复数组长度等于页面二级标签长度,则一级标签点亮
-            if (data[item.name].length === item.children.length) {
-              item.selected = true
-            }
-            item.children.forEach(function (iitem) {
-              if (data[item.name].indexOf(iitem.name) !== -1) {
-                iitem.selected = true
-              }
-            })
-          }
-        })
-      }
+      return this.$refs.industrySelectorContent.setIndustryState(data)
     },
-    // 获取选中的数据
     getSelected () {
-      const map = {}
-
-      this.industryListMap.forEach(item => {
-        const mapArr = []
-
-        if (item.level !== 0) {
-          item.children.forEach(iitem => {
-            if (iitem.selected) {
-              mapArr.push(iitem.name)
-            }
-          })
-        }
-
-        if (mapArr.length !== 0) {
-          map[item.name] = mapArr
-        }
-      })
-
-      return map
-    },
-    // 搜索找到其level1级id
-    getIndexWithString (s = '') {
-      if (!s) return
-      let giveId = ''
-      this.industryListMap.find(level1 => {
-        if (level1.name.includes(s)) {
-          giveId = level1.id
-          return level1.id
-        } else {
-          return level1.children.find(level2 => {
-            if (level2.name.includes(s)) {
-              giveId = level1.id
-              return level2
-            }
-          })
-        }
-      })
-      return giveId
+      return this.$refs.industrySelectorContent.getSelected()
     },
     onCancel () {
       this.$emit('onCancel')
@@ -302,29 +54,18 @@ export default {
     onConfirm () {
       const selected = this.getSelected()
       this.$emit('onConfirm', selected)
+    },
+    onChange (selected) {
+      this.$emit('onChange', selected)
     }
   }
 }
 </script>
 
 <style lang="scss" scoped>
-  .card {
-    .button-level-1 {
-      color: inherit;
-      font-weight: bold;
-    }
-    .button-level-2 {
-      margin: 5px;
-    }
-    .list-item-container {
-      padding: 6px 20px;
-      &:not(:last-of-type) {
-        border-bottom: 1px solid rgba(0,0,0,.05);
-      }
-      .list-item-main {
-        display: flex;
-        flex-wrap: wrap;
-      }
+  .s-line {
+    .s-header {
+      line-height: 36px;
     }
   }
 </style>

+ 408 - 0
src/components/selector/IndustrySelectorContent.vue

@@ -0,0 +1,408 @@
+<template>
+  <div class="selector-content" v-if="selectorType === 'card'" key="selector-content">
+    <div class="search-container">
+      <el-input v-model.trim="searchContent" placeholder="搜索" prefix-icon="el-icon-search"></el-input>
+    </div>
+    <div class="select-list scrollbar" ref="selectList">
+      <div
+        v-for="(item, index) in industryListMap"
+        :key="index"
+        class="list-item-container"
+      >
+        <div class="list-item-header">
+          <button
+            class="j-button-item button-level-1"
+            :class="{
+              active: item.selected,
+              [item.id]: true
+            }"
+            @click="changeIndustryState(item)"
+          >{{ item.name }}</button>
+        </div>
+        <div class="list-item-main">
+          <button
+            v-for="(iitem, iindex) in item.children" :key="999-iindex"
+            class="j-button-item button-level-2"
+            :class="{
+              active: iitem.selected,
+              [iitem.id]: true
+            }"
+            @click="changeIndustryState(iitem)"
+            >{{ iitem.name }}</button>
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="selector-content" :class="{ 'no-more': !showMore }" key="selector-content" v-else-if="selectorType === 'line'">
+    <span class="action-button show-more" @click="showMore = !showMore">
+      <span class="action-text">{{ showMore ? '收起' : '更多' }}</span>
+      <span class="el-icon-arrow-down" :class="showMore ? 'rotate180' : ''"></span>
+    </span>
+    <div
+      v-for="(item, index) in industryListMap"
+      :key="index"
+      class="list-item-container"
+    >
+      <div class="list-item-header">
+        <button
+          class="j-button-item"
+          :class="{
+            active: item.selected,
+            [item.id]: true,
+            'button-level-0': item.level === 0,
+            'button-level-1': item.level === 1,
+          }"
+          @click="changeIndustryState(item)"
+        >{{ item.name }}</button>
+      </div>
+      <div class="list-item-main">
+        <button
+          v-for="(iitem, iindex) in item.children" :key="999-iindex"
+          class="j-button-item button-level-2"
+          :class="{
+            active: iitem.selected,
+            [iitem.id]: true
+          }"
+          @click="changeIndustryState(iitem)"
+          >{{ iitem.name }}</button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Input, Icon } from 'element-ui'
+import { industryListMapExp } from '@/assets/js/selector.js'
+import { debounce, getRandomString } from '@/utils/'
+export default {
+  name: 'industry-selector-content',
+  components: {
+    [Input.name]: Input,
+    [Icon.name]: Icon
+  },
+  props: {
+    selectorType: {
+      type: String,
+      default: 'card'
+    },
+    initIndustry: {
+      type: Object,
+      default () {
+        return {
+          // '建筑工程': [
+          //     '勘察设计',
+          //     '工程施工',
+          //     '监理咨询',
+          //     '材料设备',
+          // ],
+          // '水利水电': [
+          //     '水利工程',
+          //     '发电工程',
+          //     '航运工程',
+          //     '其他工程',
+          // ]
+        }
+      }
+    }
+  },
+  data () {
+    return {
+      searchContent: '',
+      // 原始数据
+      industryListMapExp,
+      // 页面中循环的数据
+      industryListMap: [],
+      industryExp: {
+        name: '全部行业',
+        selected: false,
+        level: 0,
+        children: [],
+        id: ''
+      },
+      showMore: true
+    }
+  },
+  watch: {
+    initIndustry (newVal, oldVal) {
+      this.setIndustryState(newVal)
+    },
+    searchContent: debounce(function (newVal, oldVal) {
+      const search = newVal
+      const id = this.getIndexWithString(search)
+      if (id) {
+        this.$nextTick(() => {
+          const wrapper = document.querySelector('.industry-selector.card')
+          this.$refs.selectList.scrollTop = wrapper.querySelector(`.${id}`).offsetTop
+        })
+      }
+    }, 300)
+  },
+  created () {
+    this.initIndustryMap()
+    this.setIndustryState(this.initIndustry)
+    window.data = this
+  },
+  methods: {
+    // 整理数据列表
+    initIndustryMap () {
+      const industryListMap = []
+
+      // 全部
+      const all = JSON.parse(JSON.stringify(this.industryExp))
+      all.selected = true
+      all.id = `lv0-${getRandomString(8).toLowerCase()}`
+      industryListMap.push(all)
+
+      for (const key in this.industryListMapExp) {
+        const level1 = JSON.parse(JSON.stringify(this.industryExp))
+        level1.name = key
+        level1.level = 1
+        level1.id = `lv1-${getRandomString(8).toLowerCase()}`
+
+        const level2Arr = []
+        this.industryListMapExp[key].forEach(item => {
+          const level2 = JSON.parse(JSON.stringify(this.industryExp))
+          level2.name = item
+          level2.level = 2
+          level2.id = `lv2-${getRandomString(8).toLowerCase()}`
+          level2Arr.push(level2)
+        })
+
+        level1.children = level2Arr
+        industryListMap.push(level1)
+      }
+
+      this.industryListMap = industryListMap
+    },
+    // 按钮点击事件
+    changeIndustryState (item) {
+      // 循环所有数据,判断并改变状态
+      switch (item.level) {
+        case 0: {
+          this.setIndustryState()
+          break
+        }
+        case 1: {
+          item.selected = !item.selected
+
+          this.industryListMap[0].selected = false
+          // 二级子按钮状态跟随一级按钮
+          item.children.forEach(level2 => {
+            level2.selected = item.selected
+          })
+          break
+        }
+        case 2: {
+          item.selected = !item.selected
+
+          // 找到当前点击的父级元素
+          this.industryListMap.forEach(level1 => {
+            const selectedStateArr = []
+            if (item.level !== 0) {
+              level1.children.forEach(level2 => {
+                selectedStateArr.push(level2.selected)
+              })
+              if (selectedStateArr.indexOf(false) === -1) {
+                level1.selected = true
+              } else {
+                level1.selected = false
+              }
+            }
+          })
+
+          this.industryListMap[0].selected = false
+          break
+        }
+        default: {
+          console.log('未知level')
+        }
+      }
+
+      if (item.level !== 0) {
+        const allSelected = this.checkAllSelectedState()
+        if (allSelected.allSelected || allSelected.allNotSelected) {
+          this.setIndustryState()
+        }
+      }
+
+      this.onChange()
+    },
+    // 检查是否全部选中了/全部不选中
+    checkAllSelectedState () {
+      // 一级标签选中状态(如果一级标签全部被选中,则说明,全部按钮被选中)
+      const level1StateArr = []
+      // 所有标签选中状态
+      const allSelectedArr = []
+
+      this.industryListMap.forEach(level1 => {
+        if (level1.level !== 0) {
+          level1StateArr.push(level1.selected)
+          allSelectedArr.push(level1.selected)
+          level1.children.forEach(level2 => {
+            allSelectedArr.push(level2.selected)
+          })
+        }
+      })
+
+      return {
+        // 找不到false,就说明全部被选中
+        allSelected: level1StateArr.indexOf(false) === -1,
+        // 找不到true,就说明没有一个被选中
+        allNotSelected: allSelectedArr.indexOf(true) === -1
+      }
+    },
+    /**
+     * 初始化页面选中状态
+     * @param { Array | undefined } data 要恢复的数据
+     */
+    setIndustryState (data) {
+      // 设置全部按钮
+      if (!data || Object.keys(data).length === 0) {
+        // 其他全部设置不选中,全部按钮设置选中
+        this.industryListMap.forEach(item => {
+          item.selected = false
+          item.children.forEach(iitem => {
+            iitem.selected = false
+          })
+        })
+
+        this.industryListMap[0].selected = true
+      } else {
+        this.setIndustryState()
+        this.industryListMap[0].selected = false
+
+        this.industryListMap.forEach(item => {
+          if (data[item.name]) {
+            // 如果恢复数组长度等于页面二级标签长度,则一级标签点亮
+            if (data[item.name].length === item.children.length) {
+              item.selected = true
+            }
+            item.children.forEach(function (iitem) {
+              if (data[item.name].indexOf(iitem.name) !== -1) {
+                iitem.selected = true
+              }
+            })
+          }
+        })
+      }
+    },
+    // 获取选中的数据
+    getSelected () {
+      const map = {}
+
+      this.industryListMap.forEach(item => {
+        const mapArr = []
+
+        if (item.level !== 0) {
+          item.children.forEach(iitem => {
+            if (iitem.selected) {
+              mapArr.push(iitem.name)
+            }
+          })
+        }
+
+        if (mapArr.length !== 0) {
+          map[item.name] = mapArr
+        }
+      })
+
+      return map
+    },
+    // 搜索找到其level1级id
+    getIndexWithString (s = '') {
+      if (!s) return
+      let giveId = ''
+      this.industryListMap.find(level1 => {
+        if (level1.name.includes(s)) {
+          giveId = level1.id
+          return level1.id
+        } else {
+          return level1.children.find(level2 => {
+            if (level2.name.includes(s)) {
+              giveId = level1.id
+              return level2
+            }
+          })
+        }
+      })
+      return giveId
+    },
+    onChange () {
+      const selected = this.getSelected()
+      this.$emit('onChange', selected)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+  .s-card {
+    .button-level-1 {
+      color: inherit;
+      font-weight: bold;
+    }
+    .button-level-2 {
+      margin: 5px;
+    }
+    .list-item-container {
+      padding: 6px 20px;
+      &:not(:last-of-type) {
+        border-bottom: 1px solid rgba(0,0,0,.05);
+      }
+      .list-item-main {
+        display: flex;
+        flex-wrap: wrap;
+      }
+    }
+  }
+
+  .s-line {
+    .selector-content {
+      display: flex;
+      flex-wrap: wrap;
+      &.no-more {
+        height: 38px;
+        overflow: hidden;
+      }
+      .j-button-item {
+        border-color: transparent;
+      }
+      .button-level-0 {
+        font-weight: 700;
+        border-color: transparent;
+      }
+      .button-level-1 {
+        color: $color-text--highlight;
+        &.active {
+          color: #fff;
+          background-color: $color-text--highlight;
+        }
+      }
+      .button-level-2 {
+        &.active {
+          background: rgba(44,183,202,0.10);
+        }
+      }
+    }
+    .list-item-container {
+      position: relative;
+      margin: 0 4px;
+      display: flex;
+      flex-wrap: wrap;
+      &:not(:last-of-type)::after {
+        content: '';
+        position: absolute;
+        right: -6px;
+        top: 50%;
+        width: 1px;
+        height: 16px;
+        background-color: #E3E4E6;
+        margin-top: -8px;
+      }
+      .list-item-main {
+        display: flex;
+        flex-wrap: wrap;
+      }
+    }
+  }
+</style>

+ 56 - 28
src/components/selector/SelectorCard.vue

@@ -54,36 +54,44 @@ export default {
       position: relative;
       display: flex;
       flex: 1;
-    }
-    &::v-deep {
-      // 子组件按钮公共样式
-      .j-button-item {
-        display: flex;
-        align-items: center;
-        margin: 6px 5px;
-        padding: 2px 6px;
-        line-height: 20px;
-        border-radius: 4px;
-        font-size: 14px;
-        text-align: center;
-        color: #606266;
-        background-color: #fff;
-        border: 1px solid rgba(0,0,0,.05);
-        cursor: pointer;
-        &.global {
-          padding: 6px 8px;
-          height: 24px;
-          line-height: 24px;
-          font-weight: 700;
-          color: inherit;
-          border-color: rgba(0,0,0,.05);
+
+      &::v-deep {
+        // 子组件按钮公共样式
+        .j-button-item {
+          display: flex;
+          align-items: center;
+          margin: 6px 5px;
+          padding: 2px 6px;
+          line-height: 20px;
+          border-radius: 4px;
+          font-size: 14px;
+          text-align: center;
+          
+          background-color: #fff;
+          border: 1px solid rgba(0,0,0,.05);
+          cursor: pointer;
+          &.global {
+            padding: 6px 8px;
+            height: 24px;
+            line-height: 24px;
+            font-weight: 700;
+            color: inherit;
+            border-color: rgba(0,0,0,.05);
+          }
+          &.hover:hover {
+            color: #2abed1;
+          }
+          &.active {
+            color: #2abed1;
+            border-color: #2abed1;
+          }
         }
-        &.hover:hover {
-          color: #2abed1;
+
+        [class^=el-icon-] {
+        transition: transform 0.2s ease;
         }
-        &.active {
-          color: #2abed1;
-          border-color: #2abed1;
+        .rotate180 {
+          transform: rotate(180deg);
         }
       }
     }
@@ -142,6 +150,9 @@ export default {
     }
 
     &::v-deep {
+      .j-button-item {
+        color: #606266;
+      }
       // 输入框公共样式
       .el-input__inner {
         padding-left: 42px;
@@ -192,5 +203,22 @@ export default {
     .selector-card-header {
       margin-right: 10px;
     }
+    .selector-content {
+      position: relative;
+    }
+    ::v-deep .action-button {
+      display: flex;
+      align-items: center;
+      position: absolute;
+      top: 8px;
+      right: 0;
+      font-size: 12px;
+      line-height: 20px;
+      color: #686868;
+      cursor: pointer;
+      .action-text {
+        margin-right: 4px;
+      }
+    }
   }
 </style>