<!--
 * @Author: wlo_o
 * @Date: 2022-01-24 14:06:53
 * @LastEditors: Jone
 * @LastEditTime: 2022-07-04 15:57:05
 * @FilePath: /frontend/lib/components/MdCategory.vue
 * @Description: 分类（展示/维护）
 *
 * |事件名称| 说明 | 回调参数|
 * | --- | --- | --- |
 * |change|tag有变化时触发|(values, groupObject)|
 * |click|tag点击后触发|(id,action)|
 *
 * Copyright (c) 2022 by 青岛百洋智能科技股份有限公司, All Rights Reserved.
-->

<template>
  <div
    v-loading="loading"
    class="oecp-md-category"
  >
    <el-row
      v-if="editable||!!title||!!$slots['title']"
      style="padding:10px;background: #ebeff2;"
    >
      <el-col :span="12">
        <template v-if="!!$slots.title">
          <slot name="title"></slot>
        </template>
        <div
          v-else-if="!!title"
          class="title-style"
        >{{title}}</div>
        <div v-else>&nbsp;</div>
      </el-col>
      <el-col
        v-if="editable"
        :span="12"
      >
        <el-row
          type="flex"
          justify="end"
        >
          <el-button
            v-oecp-auth:[{type:stateType}]="'category.addRoot'"
            @click="handleTabsAdd()"
            type="primary"
            icon="el-icon-plus"
            class="addBtn"
            size="medium"
          >添加标签类别</el-button>
        </el-row>
      </el-col>
    </el-row>
    <!-- <el-divider v-if="editable" /> -->
    <el-tabs
      v-if="editable||showTabs"
      v-model="stateType"
      :style="editable?{'margin-right':'100px'}:{}"
      @tab-click="handleTabClick"
      type="card"
    >
      <el-tab-pane
        v-for="(item,idx) in typeConfig.list"
        :key="`cat_${idx}`"
        :name="item.type"
      >
        <span slot="label">{{item.name}}</span>
      </el-tab-pane>
    </el-tabs>
    <el-row
      v-if="root&&root.length"
      style="background:#fff;"
    >
      <el-row
        v-for="(item,index) in root"
        :key="`root_${index}`"
        class="mb5 bb1px"
        style="margin:0 10px 0 20px"
      >
        <el-col
          :span="24"
          class="tagInner flex"
        >
          <img src="../common/img/tagImg.png" />
          <span style="word-wrap: break-word">{{item.name}}</span>
          <i
            v-show="!loading&&stateEditable&&item.isSys!=1"
            v-oecp-auth:[{type:stateType}]="'category.del'"
            @click="!disabled&&handlerFind(item)"
            class="el-icon-close oecp_category_delete"
          />
          <!-- <el-tag
            v-if="showSelectAllTag"
            :effect="rootCount[item.id]==0?'dark':'plain'"
            style="margin-left:10px"
            @click="handleRootClick(item)"
          >全部{{rootCount[item.id]}}</el-tag> -->
          <el-checkbox
            style="margin-left:10px"
            v-if="showSelectAllTag"
            :value="rootCount[item.id]==0"
            @change="handleRootClick(item)"
          >全部</el-checkbox>
        </el-col>
        <el-col
          :span="24"
          class="tag-row"
          :style="nodeGroupExpand[item.id]?nodeGroupExpand[item.id].style:{}"
        >
          <div
            v-if="displayType=='tag'&&nodes[item.id]&&nodes[item.id].length"
            :ref="`tag_items_${item.id}`"
            style="padding: 4px 0 6px"
          >
            <md-category-tag
              v-for="(obj,idx) in nodes[item.id]"
              :key="`tag_${item.id}_${idx}`"
              :editable="stateEditable"
              :disabled="disabled"
              :values="stateValues"
              :data="obj"
              :type="stateType"
              @click="handleNodeClick(obj,item)"
              @success="handleTabClick"
            />
          </div>
          <template v-else-if="displayType=='input'&&nodeGroup[item.id]&&nodeGroup[item.id].list&&nodeGroup[item.id].list.length">
            <el-checkbox-group
              v-if="nodeGroup[item.id].inputType=='checkbox'"
              v-model="nodeGroup[item.id].selected"
              @change="(v)=>handleCheckboxGroupChange(v,item)"
              class="mb5 mdCatCheckBox"
            >
              <el-checkbox
                v-for="(node,idx) in nodes[item.id]"
                :disabled="disabled"
                :key="`node_${item.id}_${idx}`"
                :label="node.id"
                class="mb5"
              >{{node.name}}</el-checkbox>
            </el-checkbox-group>
            <el-radio-group
              v-if="nodeGroup[item.id].inputType=='radio'"
              v-model="nodeGroup[item.id].selected"
              @change="(v)=>handleRadioGroupChange(v,item)"
              class="mb5"
            >
              <el-radio
                v-for="(node,idx) in nodes[item.id]"
                :key="`node_${item.id}_${idx}`"
                :disabled="disabled"
                :label="node.id"
                class="mb5"
              >{{node.name}}</el-radio>
            </el-radio-group>
          </template>
          <div v-else>&nbsp;</div>
        </el-col>
        <el-col
          v-if="editable||(nodeGroup[item.id]&&nodeGroup[item.id].list.length>1)"
          style="display:none;"
          :span="2"
        >
          <el-button
            v-if="editable"
            v-oecp-auth:[{type:stateType}]="'category.addNode'"
            :key="`category.addNode.${new Date().getTime()}`"
            @click="handleTabsAdd({parentId:item.id})"
            icon="el-icon-plus"
            type="primary"
            size="mini"
            circle
          />
          <el-link
            v-else-if="showExpand"
            v-show="nodeGroupExpand[item.id].visible"
            :underline="false"
            @click="handleExpandClick(item.id)"
            type="primary"
          >
            <template v-if="nodeGroupExpand[item.id].expanded">收起 <i class="el-icon-arrow-up" /></template>
            <template v-else>展开 <i class="el-icon-arrow-down" /></template>
          </el-link>
        </el-col>
      </el-row>
    </el-row>
    <el-row v-else>
      <el-empty v-if="editable">
        <el-button
          v-oecp-auth:[{type:stateType}]="'category.addRoot'"
          @click="handleTabsAdd()"
          type="primary"
          icon="el-icon-plus"
          class="addBtn"
          size="medium"
        >添加标签类别</el-button>
      </el-empty>
    </el-row>
    <md-category-dialog
      :visible="dialogVisible"
      :data="dialogData"
      :title="dialogTitle"
      @close="dialogVisible=false"
      @success="handleTabClick"
    />
  </div>
</template>
<script>
import { t as lang } from '../locale/index.js'
import OptionsMixin from '../mixins/OptionsMixin'
import get from 'lodash/get'
import isEmpty from 'lodash/isEmpty'
import isArray from 'lodash/isArray'
import { fetchApi } from '../common/apis.js'

export default {
  mixins: [OptionsMixin],
  props: {
    title: { type: [String, null], default: '' }, // 标题
    disabled: { type: Boolean, default: false }, // 禁用，不可点击，不可操作
    editable: { type: Boolean, default: false }, // 能否编辑,用于分类管理时使用。默认不可编辑
    type: { type: [String, Number, null], default: null }, // 元素类型
    showTabs: { type: Boolean, default: false }, // 显示顶部页签
    showSelectAllTag: { type: Boolean, default: false }, // 父标后面显示选择「全部」
    displayType: { type: String, default: 'tag', validator: v => ['tag', 'input'].includes(v) }, // 显示方式
    showExpand: { type: Boolean, default: true }, // 显示展开
    values: { type: [Array, null], default: () => [] },
    types: { type: [Array, null], default: null }, // 显示的类型，如果为空显示全部
  },
  computed: {
    typeConfig: function () {
      let arr = this.getMbOptionVar('typeConfig') || { list: [] }
      let { types } = this
      if (isEmpty(types)) return arr
      return { ...arr, list: arr.list.filter(item => types.includes(item.type)) }
    },
  },
  watch: {
    values: {
      handler: function (arr) {
        this.stateValues = arr
      },
      immediate: true,
      deep: true,
    },
    type: function (type) {
      this.stateType = type
    },
    stateType: function () {
      this.handleTabClick()
    },
    stateValues: {
      handler: function (arr) {
        this.displayType === 'input' && this.backfillValues()
      },
      deep: true,
    },
    nodes: {
      handler: function (arr) {
        this.displayType === 'input' && this.backfillValues()
      },
      deep: true,
    },
  },
  data() {
    return {
      lang,
      loading: false,
      stateEditable: this.editable,
      stateType: this.type,
      dialogVisible: false,
      root: [], // 根节点
      nodes: {}, // 子节点 key: 父级 id
      nodeGroup: {}, // 每6个一组 key: 父级 id
      nodeGroupExpand: {}, // 展开标记 key: 父级 id
      stateValues: this.values || [], // 选中的标签 id
      searchValues: [], // 分组格式的值，查询使用
      rootCount: {}, // 计数
      dialogData: {},
      dialogTitle: '',
      expandVisible: {},
    }
  },
  methods: {
    handleExpandClick(id) {
      let { expanded } = this.nodeGroupExpand[id]
      this.nodeGroupExpand[id] = {
        ...this.nodeGroupExpand[id],
        expanded: !expanded,
        style: expanded ? { height: '2.1em', overflow: 'hidden' } : { height: 'auto' },
      }
    },
    initExpandBtn() {
      if (isEmpty(this.root)) return
      let that = this
      let arr = JSON.parse(JSON.stringify(this.nodeGroupExpand))
      this.root.forEach(item => {
        let ref = that.$refs[`tag_items_${item.id}`]
        if (isEmpty(ref)) return
        let height = isArray(ref) ? ref[0].clientHeight : ref.clientHeight
        if (height > 50) {
          arr[item.id].visible = true
        }
      })
      this.nodeGroupExpand = arr
    },
    backfillValues() {
      // 回填数据
      let { stateValues: values, nodes, nodeGroup } = this
      if (isEmpty(values) || isEmpty(nodes)) return
      for (let rootId in nodeGroup) {
        let arr = nodes[rootId] || [] // 所有末级节点
        let ids = arr.reduce((l, o) => {
          return [...l, o.id]
        }, []) // 所有可选主键
        let inputType = nodeGroup[rootId].inputType
        let selected = inputType === 'radio' ? '' : []
        for (let i = 0; i < ids.length; i++) {
          if (!values.includes(ids[i])) continue
          if (inputType === 'radio') {
            selected = ids[i]
            break
          } else {
            selected.push(ids[i])
          }
        }
        nodeGroup[rootId] = { ...nodeGroup[rootId], selected }
      }
    },
    handleTabsAdd(data = {}) {
      this.dialogData = { name: '', type: this.stateType, ...data }
      this.dialogTitle = Object.keys(data).length ? '添加标签' : '添加标签类别'
      this.dialogVisible = true
    },
    handleDialogOk() {
      this.$refs.form.validate(valid => {
        if (!valid) return false
      })
    },
    handleRootClick(root) {
      console.log(root)
      if (this.rootCount[root.id] === 0) return false
      let delKeys = []
      let values = []
      this.nodes[root.id].forEach(item => {
        delKeys.push(item.id)
      })
      this.stateValues.forEach(id => {
        !delKeys.includes(id) && values.push(id)
      })
      this.stateValues = values
      this.rootCount[root.id] = 0
      this.searchValues = this.searchValues.filter(item => item.id !== root.id)
      this.$emit('change', this.stateValues, this.searchValues)
    },
    handleNodeClick(node, root) {
      let arr = [...this.stateValues]
      let { id } = node
      let { id: rootId } = root
      let index = arr.indexOf(id)
      let action = ''
      let idx = this.searchValues.findIndex(o => o.id === rootId)
      if (index > -1) {
        arr.splice(index, 1)
        this.rootCount[rootId] -= 1
        action = 'remove'
        if (idx !== -1) {
          this.searchValues[idx].children = (get(this.searchValues[idx], 'children') || []).filter(v => v !== id)
          this.searchValues[idx].children.length === 0 && this.searchValues.splice(idx, 1)
        }
      } else {
        arr.push(id)
        this.rootCount[rootId] += 1
        action = 'add'
        let list = idx === -1 ? [id] : get(this.searchValues[idx], 'children')
        let children = [...new Set([...(list || []), id])]
        if (idx === -1) {
          this.searchValues.push({ id: rootId, children })
        } else {
          this.searchValues[idx].children = children
        }
      }
      this.stateValues = arr
      this.$emit('change', this.stateValues, this.searchValues)
      this.$emit('click', id, action)
    },
    handleCheckboxGroupChange(val, root) {
      // 查出差异的值
      let arr = this.nodes[root.id] // 所有末级节点
      let ids = arr.reduce((l, o) => {
        return [...l, o.id]
      }, []) // 所有可选主键
      let lastIds = this.stateValues.filter(v => ids.includes(v)) || [] // 之前选择的数据
      let addId = val.find(v => !lastIds.includes(v))
      let delId = lastIds.find(v => !val.includes(v))
      if (addId) {
        this.stateValues.push(addId)
        this.$emit('click', addId, 'add')
      }
      if (delId) {
        this.stateValues.splice(this.stateValues.findIndex(v => v === delId))
        this.$emit('click', delId, 'remove')
      }
      this.$emit('change', this.stateValues)
    },
    handleRadioGroupChange(id, root) {
      let arr = this.nodes[root.id] // 所有末级节点
      let ids = arr.reduce((l, o) => [...l, o.id], []) // 所有可选主键
      let idx = this.stateValues.findIndex(v => ids.includes(v)) // 之前选择的数据
      if (idx !== -1 && this.stateValues[idx] !== id) {
        // 去除掉历史数据
        this.$emit('click', this.stateValues[idx], 'remove')
        this.stateValues.splice(idx, 1)
      }
      this.stateValues.push(id)
      this.$emit('change', this.stateValues)
      this.$emit('click', id, 'add')
    },
    handleTabClick() {
      let that = this
      if (this.loading) return
      this.stateValues = []
      this.$emit('change', this.stateValues)
      this.nodes = {}
      this.nodeGroup = {}
      this.nodeGroupExpand = {}
      this.expandVisible = {}
      this.rootCount = {}
      let query = { parentId: '0', type: this.stateType, pageSize: 1e5 }
      this.loading = true
      let dfd1 = new Promise(function (resolve, reject) {
        // 加载根分类
        fetchApi
          .call(this, '/category/pageList', query)
          .then(res => {
            that.root = get(res, 'records') || []
            let obj = { nodes: {}, nodeGroup: {}, rootCount: {}, nodeGroupExpand: {} }
            that.root.forEach(item => {
              obj.nodes[item.id] = []
              obj.rootCount[item.id] = 0
              obj.nodeGroup[item.id] = { inputType: 'radio', selected: '', list: [] }
              obj.nodeGroupExpand[item.id] = {
                visible: false,
                expanded: !!that.editable || !that.showExpand,
                // ...(that.displayType !== 'input' && !that.editable && { style: { height: '2.1em', overflow: 'hidden' } }),
                ...(that.displayType !== 'input'),
              }
            })
            that.nodeGroupExpand = obj.nodeGroupExpand
            that.nodeGroup = obj.nodeGroup
            that.rootCount = obj.rootCount
            that.nodes = obj.nodes
            resolve()
          })
          .catch(function (err) {
            that.loading = false
            reject(err)
          })
      })
      let dfd2 = new Promise(function (resolve, reject) {
        // 获取分类扩展，能否单选还是多选
        dfd1.then(function () {
          if (that.displayType === 'input') {
            // 标签显示是在表单编辑时使用，需要根据扩展参数
            let parentIds = that.root.reduce((arr, o) => [...arr, o.id], [])
            fetchApi.call(this, '/category/extList', { parentIds, extName: 'multiple' }).then((list = []) => {
              list.forEach(item => {
                if (item.extValue && item.extValue === 'true') {
                  that.nodeGroup[item.parentId] = { ...that.nodeGroup[item.parentId], inputType: 'checkbox', selected: [] }
                }
              })
              resolve()
            })
          } else {
            // 标签是用在作为查询条件筛选
            resolve()
          }
        })
      })
      dfd2.then(function () {
        let dfdList = []
        that.root.forEach(item => {
          dfdList.push(that.loadChildren(item.id))
        }) // 加载子分类
        Promise.all(dfdList).then(
          () => {
            that.loading = false
            that.initExpandBtn()
          },
          () => {
            that.loading = false
          },
        )
      })
      this.$emit('update:type', this.stateType)
    },
    loadChildren(parentId) {
      let that = this
      return fetchApi.call(this, '/category/pageList', { parentId, type: this.stateType, pageSize: 1e5 }).then(res => {
        let list = get(res, 'records') || []
        that.nodes[parentId] = list
        that.nodeGroup[parentId].list = list
      })
    },
    handlerFind(item) {
      // 先查询是否有关联的数据，再进行删除的提示操作
      let that = this
      fetchApi
        .call(this, '/category/findCategoryRelationships', { categoryId: item.id, type: item.type })
        .then(function (res) {
          that.handleDel(item.id, res)
        })
        .catch(err => {
          console.log(err)
        })
        .finally(() => {})
    },
    handleDel(id, data) {
      // 如果data大于0 就说明有关联
      let that = this
      let msg = data > 0 ? '此类别有关联数据，是否继续永久删除?' : '此操作将永久删除该标签类别, 是否继续?'
      this.$confirm(msg, '提示', {
        type: 'warning',
        confirmButtonText: '确定',
        cancelButtonText: '取消',
      }).then(() => {
        that.loading = true
        fetchApi
          .call(this, '/category/delete', { id })
          .then(function (res) {
            that.$message({ type: 'success', message: '删除成功!' })
            setTimeout(function () {
              that.handleTabClick()
            }, 200)
          })
          .catch(err => {
            console.log(err)
          })
          .finally(() => {
            that.loading = false
          })
      })
    },
  },
  created() {
    this.stateType = this.type || get(this.typeConfig, 'list[0].type')
    this.stateType && this.handleTabClick()
  },
}
</script>
<style scoped>
.title-style {
  /* padding-top: 6px; */
  font-size: 18px;
  /* font-weight: 600; */
}
.mb5 {
  margin-bottom: 5px;
}
.bb1px {
  border-bottom: 1px dotted #ddd;
}
.oecp_category_delete {
  cursor: pointer;
  color: #409eff;
}
.tagInner {
  padding: 8px 0 6px;
  display: flex;
  align-items: center;
  font-size: 16px;
}
.tagInner img {
  height: 12px;
  width: 12px;
  margin-right: 6px;
}
/deep/.tagInner .el-checkbox .el-checkbox__inner {
  border-radius: 100% !important;
  border: 1px solid #979797;
  height: 16px;
  width: 16px;
}
.tagInner .el-checkbox .el-checkbox__input {
  border-radius: 100%;
}
/deep/.el-checkbox__label {
  padding-left: 4px;
  color: #323232 !important;
}
/deep/.el-checkbox__input.is-checked .el-checkbox__inner {
  background-color: #fff;
  border-color: #979797;
}
/deep/.el-checkbox__inner::after {
  border: none;
  background: #acacac;
  border-radius: 100%;
  width: 9px;
  height: 9px;
  top: 17%;
  left: 17.85%;
}
/deep/.is-checked .el-checkbox__inner::after {
  border: none;
  background: #1a63eb;
  border-radius: 100%;
  width: 9px;
  height: 9px;
  top: 17%;
  left: 17.85%;
}
/* tab start */
.el-tabs {
  width: 100%;
}
/deep/.el-tabs__nav-scroll {
  background-color: #ebeff2;
  /* padding-top: 10px; */
}

/deep/.el-tabs__nav,
/deep/.el-tabs__item {
  border: none !important;
}
/deep/.el-tabs--card > .el-tabs__header .el-tabs__nav {
  background: #e4e5e6 !important;
  border-radius: 0 !important;
  border-top-left-radius: 8px !important;
  border-top-right-radius: 8px !important;
  overflow: hidden;
}

/deep/.el-tabs__item {
  background: #e4e5e6;
  color: #1b275a;
  width: 148px;
  height: 46px;
  padding: 0 !important;
  text-align: center;
  line-height: 48px;
  border: none;
  position: relative;
}
/deep/.el-tabs__item:hover {
  color: #0c5aea;
}
/deep/.el-tabs__item:after {
  content: '';
  width: 1px;
  height: 46%;
  position: absolute;
  left: -1px;
  top: 28%;
  background: #728bce;
}
/deep/.el-tabs__item:first-of-type:after,
/deep/.el-tabs__item.is-active:after {
  display: none;
}
/deep/.el-tabs__item.is-active:before {
  content: '';
  width: 1px;
  height: 46%;
  position: absolute;
  right: 0;
  top: 28%;
  background: #fff;
  z-index: 1;
}
/deep/.el-tabs__item.is-active {
  background: #fff;
  color: #0c5aea;
  font-size: 14px;
  background-size: 100% 100%;
  border-top-left-radius: 6px;
  border-top-right-radius: 6px;
}

/deep/.el-tabs--card > .el-tabs__header {
  border: none;
  margin: 0;
}

/* tab end */
.oecp-md-category {
  height: 100%;
  background: #fff;
}
.addBtn {
  background: #2468f2;
  border-color: #2468f2;
  padding: 0 20px;
}
.addBtn:hover {
  background: #2469f2d6;
  border-color: #2469f2d6;
}
.el-radio {
  border: 1px solid #90afff;
  border-radius: 4px;
  height: 24px;
  line-height: 24px;
  padding: 0 6px;
  color: #497ee1;
  background: #edf2ff;
  margin-right: 10px;
  margin-bottom: 10px;
}
/deep/.el-radio__label {
  padding: 0 !important;
}
/deep/.is-checked.el-radio {
  background: #73b9ff;
  border-color: #7399ff;
}
/deep/.is-checked.el-radio .el-radio__label {
  color: #fff !important;
}
/deep/.el-radio__input {
  display: none !important;
}
.mdCatCheckBox .el-checkbox {
  border: 1px solid #90afff;
  border-radius: 4px;
  height: 24px;
  line-height: 24px;
  padding: 0 6px;
  color: #497ee1;
  background: #edf2ff;
  margin-right: 10px;
  margin-bottom: 10px;
}
/deep/.mdCatCheckBox .el-checkbox__input {
  display: none !important;
}
/deep/.mdCatCheckBox .is-checked .el-checkbox__label {
  color: #fff !important;
}
/deep/.mdCatCheckBox .is-checked {
  background: #73b9ff;
  border-color: #7399ff;
}
/deep/.mdCatCheckBox .is-disabled .el-checkbox__label {
  color: #c0c4cc !important;
}
/deep/.el-icon-close {
  font-size: 18px;
}
</style>
