<template>
  <section class="ai-area">
    <div v-if="inputClicker&&!$scopedSlots.default" @click="chooseArea" class="input-clicker">
      <el-row type="flex" justify="space-between">
        <div class="prepend">
          <i style="font-size: 16px" class="iconfont iconLocation"/>
          切换地区
        </div>
        <div class="content nowrap-text fill" v-text="fullName"/>
        <i class="iconfont iconChange pad-r10"/>
      </el-row>
    </div>
    <el-button v-else-if="!customClicker&&!$scopedSlots.default" class="area-btn" type="primary" size="mini"
               @click="chooseArea">
      {{ btnShowArea ? selectedArea.name : "切换地区" }}
    </el-button>
    <a class="custom-clicker" v-else @click="chooseArea">
      <slot :areaname="selectedArea.name" :fullname="fullName" :id="selected"/>
    </a>
    <ai-dialog :visible.sync="dialog" title="选择地区" width="60%" @onConfirm="confirmArea" :modal="mask"
               @open="selected=(value||'')">
      <ai-highlight content="您当前选择&nbsp;@v" :value="selectedArea.name" color="#333" bold/>
      <div class="area_edge">
        <div class="area-box" v-for="ops in showOps">
          <h2 v-text="ops.header"/>
          <div class="area-item" :class="{selected: selectedMap.includes(area.id)}" v-for="area in ops.list"
               @click="getChildrenAreas(area)">
            <ai-badge>
              <span>{{ area.name }}</span>
              <div slot="badge" v-if="showBadge&&area.tipName" :class="getLabelClassByLabelType(area.labelType)">
                {{ area.tipName }}
              </div>
            </ai-badge>
          </div>
        </div>
      </div>
    </ai-dialog>
  </section>
</template>
<script>
import instance from "../../../lib/js/request";
import Area from "../../../lib/js/area";

export default {
  name: 'AiArea',
  inject: {
    elFormItem: {default: ""},
    elForm: {default: ''},
  },
  model: {
    prop: 'value',
    event: 'change'
  },
  props: {
    instance: {default: () => instance},
    action: String,
    areaLevel: {type: [Number, String], default: 5},
    btnShowArea: {type: Boolean, default: false},
    customClicker: {type: Boolean, default: false},
    disabled: {type: Boolean, default: false},
    hideLevel: {type: [Number, String], default: 0},
    inputClicker: {type: Boolean, default: true},
    provinceAction: String,
    separator: {type: String, default: ""},
    showBadge: {type: Boolean, default: true},
    value: String,
    valueLevel: {type: [Number, String], default: -1},
    root: String,
    mask: {type: Boolean, default: true}
  },
  data() {
    return {
      selected: null,
      areaOps: [],
      fullName: '',
      dialog: false,
      ProvinceCityCounty: [],
    }
  },
  computed: {
    rootArea: v => new Area(v.root),
    currentArea: v => v.selected || v.value,
    startLevel: v => Math.max(Number(v.hideLevel), 0, v.rootArea.level),//地区最高可选行政地区等级
    endLevel: v => Number(v.areaLevel) || 0,//地区最低可选行政地区等级
    selectedArea: v => new Area(v.currentArea, v.hashMap),
    selectedMap: v => v.selectedArea.areaMap,
    validateState: v => ['', 'success'].includes(v.elFormItem?.validateState),
    hashMap() {
      //地区数据缓存器,用于快速获取数据
      const hash = {}
      this.areaOps.flat().map(e => hash[e.id] = e)
      return hash
    },
    showOps() {
      const levelLabels = {
        0: "省/直辖市",
        1: "市",
        2: "县/区",
        3: "乡/镇/街道",
        4: "村/社区"
      }
      let ops = this.areaOps.map((list, i) => ({
        header: levelLabels[i], i, list
      })).slice(Math.max(0, this.startLevel), this.endLevel)
      if (this.startLevel > 0 && ops.length > 0) {
        const tmp = this.$copy(ops[0]?.list?.[0] || {})
        if (this.startLevel >= ops[0].i) {
          const opsMap = this.selectedMap.length > 0 ? this.selectedMap : this.rootArea.areaMap
          ops[0].list = [this.hashMap[opsMap[ops[0].i]]].filter(Boolean) || []
        } else {
          const prev = +tmp.type - 1
          const prevId = tmp.parentId
          prev > -1 && ops.unshift({header: levelLabels[prev], list: [this.hashMap[prevId]]})
        }
      }
      return ops
    }
  },
  watch: {
    value: {
      immediate: true,
      handler(v) {
        this.dispatch('ElFormItem', 'el.form.change', [v]);
        this.initAreaName()
      }
    }
  },
  methods: {
    dispatch(componentName, eventName, params) {
      let parent = this.$parent || this.$root;
      let name = parent.$options.componentName;

      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;

        if (parent) {
          name = parent.$options.componentName;
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },
    chooseArea() {
      if (this.disabled) return
      this.selected = this.$copy(this.value)
      this.initOptions().then(() => this.dialog = true)
    },
    confirmArea() {
      if (this.valueLevel > -1) {
        this.$emit("change", this.selectedMap[this.valueLevel])
      } else {
        this.$emit("change", this.selected)
      }
      this.$emit("area", this.selected, this.selectedArea);
      this.dialog = false
    },
    getChildrenAreas(area) {
      this.selected = area.id;
      const level = Area.getLevelByAreaId(area.id);
      if (level < 4) {
        this.getAreasByParentId(area.id).then(list => {
          this.areaOps.splice(level + 1, 5, list)
        })
      }
    },
    getAreasByParentId(id) {
      const level = Area.getLevelByAreaId(id)
      return new Promise(resolve => {
        if (level < 2) {
          this.getProvinceCityCounty().then(() => {
            resolve(this.ProvinceCityCounty.filter(e => e.parentId == id))
          })
        } else {
          this.instance.post(this.action || "/admin/area/queryAreaByParentId", null, {
            withoutToken: true,
            params: {id}
          }).then(res => {
            if (res?.data) {
              resolve(res.data)
            }
          })
        }
      })
    },
    getLabelClassByLabelType(type) {
      let cls = "badge-label"
      switch (type) {
        case '1':
          cls += ' label-town'
          break;
        case '3':
          cls += ' label-village'
          break;
        default:
          cls += ' label-poor'
          break
      }
      return cls
    },
    getProvinceCityCounty() {
      return new Promise(resolve => {
        if (localStorage.getItem("ProvinceCityCounty")) {
          resolve(JSON.parse(localStorage.getItem("ProvinceCityCounty")))
        } else {
          this.instance.post(this.provinceAction || "/admin/area/queryProvinceListContainCity", null, {
            withoutToken: true
          }).then(res => {
            if (res && res.data) {
              localStorage.setItem("ProvinceCityCounty", JSON.stringify(res.data))
              resolve(res.data)
            }
          })
        }
      }).then(list => this.ProvinceCityCounty = list)
    },
    initOptions() {
      this.areaOps = []
      const opsMap = this.selectedMap.length > 0 ? this.selectedMap : this.rootArea.areaMap
      let map = {};
      return Promise.all([null, ...opsMap].map((id, i) => this.getAreasByParentId(id).then(list => map[i] = list))).then(() => {
        this.areaOps = Object.values(map)
      })
    },
    initAreaName() {
      if (this.value) {
        Area.createByAction(this.currentArea, this.instance).then(names => {
          this.selectedArea.getName(names)
          this.fullName = this.selectedArea.nameMap.join(this.separator)
          this.$emit("update:name", this.selectedArea.name)
          this.$emit("fullname", this.fullName)
        })
      }
    }
  }
}
</script>
<style lang="scss" scoped>
.ai-area {
  .area-btn {
    box-shadow: 0 2px 8px 0 rgba(76, 132, 255, 0.6);
  }

  .input-clicker {
    width: 320px;
    cursor: pointer;
    border: 1px solid #D0D4DC;
    line-height: 32px;
    border-radius: 2px;
    font-size: 14px;

    .prepend {
      background: rgba(245, 245, 245, 1);
      width: auto;
      border-right: 1px solid #D0D4DC;
      padding: 0 8px;
      white-space: nowrap;

    }

    .content {
      text-align: left;
      padding-left: 14px;
      padding-right: 8px;
      direction: rtl;
    }

    .suffix {
      width: auto;
      padding: 0 12px
    }

    &:hover {
      border-color: $primaryColor;
    }
  }

  .custom-clicker {
    text-decoration: none;
    cursor: pointer;
    padding: 3px;
  }

  .area_edge {
    max-height: 350px;
    overflow-y: auto;
    white-space: normal;
  }

  .area-box {
    box-shadow: 0px -1px 0px 0px rgba(238, 238, 238, 1);
    padding: 16px 0 8px 0;

    & > section {
      font-size: 0;
    }

    & > h2 {
      color: rgba(51, 51, 51, 1);
      line-height: 22px;
      margin-bottom: 8px;
      font-size: 14px;
      font-weight: 600;
    }

    .area-item {
      display: inline-block;
      border-radius: 2px;
      border: 1px solid #D0D4DC;
      margin: 8px 8px 8px 0;
      padding: 3px 10px;
      cursor: pointer;
      text-align: center;
      line-height: normal;
      font-size: 14px;

      &:hover {
        color: rgba($primaryColor, .8);
        border-color: rgba($primaryColor, .8);
      }
    }

    a {
      text-decoration: none;
      border-radius: 4px;
      border: 1px solid #ddd;
      line-height: normal;
      margin: 5px;
      padding: 3px;
      cursor: pointer;

      span {
        margin: 0 10px;
      }

      &:hover {
        color: rgba($primaryColor, .8);
        border-color: rgba($primaryColor, .8);
      }
    }

    .selected {
      color: rgba($primaryColor, .8);
      border-color: rgba($primaryColor, .8);
    }
  }

  .badge-label {
    font-size: 12px;
    font-weight: bold;
    border-radius: 15px;
    color: #fff;
    width: 12px;
    letter-spacing: 10px;
    text-align: center;
    overflow: hidden;
    padding: 3px 5px;
    white-space: nowrap;
    transition: width 1s, letter-spacing 0.05s;

    &.label-town {
      background: rgba($primaryColor, .8);
    }

    &.label-village {
      background: rgba($primaryColor, .8);
    }

    &.label-poor {
      background: #ffb14c;
    }

    &:hover {
      width: initial;
      letter-spacing: unset;
    }
  }
}
</style>
