<template>
  <d2-container>
    <template #header>
      <el-form :inline="true" :model="searchForm" ref="searchFormRef" size="mini">
        <el-form-item :label="$t(key('link_type_module'))" prop="module">
          <el-radio-group v-model="searchForm.module" @change="handleSearch" size="small">
            <el-radio-button :label="$t(key('admin'))"></el-radio-button>
            <el-radio-button :label="$t(key('pda'))"></el-radio-button>
          </el-radio-group>
        </el-form-item>

        <el-form-item :label="$t(key('status'))" prop="status">
          <el-select
            v-model="searchForm.status"
            :placeholder="$t(key('please_select'))"
            style="width: 120px;"
            clearable
          >
            <el-option :label="$t(key('enable'))" :value="1" />
            <el-option :label="$t(key('disable'))" :value="0" />
          </el-select>
        </el-form-item>

        <el-form-item :label="$t(key('navigation_property'))" prop="is_navi">
          <el-select
            v-model="searchForm.is_navi"
            :placeholder="$t(key('please_select'))"
            style="width: 120px;"
            clearable
          >
            <el-option :label="$t(key('navigation_visible'))" :value="1" />
            <el-option :label="$t(key('navigation_hidden'))" :value="0" />
          </el-select>
        </el-form-item>

        <el-form-item :label="$t(key('menu_depth'))" prop="level">
          <el-input-number
            v-model="searchForm.level"
            controls-position="right"
            :min="0"
            style="width: 100px;"
          />
        </el-form-item>

        <el-form-item>
          <el-button
            type="primary"
            icon="el-icon-search"
            :disabled="loading"
            @click="handleSearch"
          >
            {{ $t(key('query')) }}
          </el-button>
        </el-form-item>

        <el-form-item>
          <el-button icon="el-icon-refresh" @click="handleReset">
            {{ $t(key('reset')) }}
          </el-button>
        </el-form-item>
      </el-form>
    </template>

    <div class="menu-config">
      <el-form :inline="true" size="small" @submit.native.prevent>
        <el-form-item v-if="auth.create">
          <el-button
            icon="el-icon-plus"
            :disabled="loading"
            @click="handleCreateTop"
          >
            {{ $t(key('add_top_menu')) }}
          </el-button>
        </el-form-item>

        <el-form-item>
          <el-button-group>
            <el-button icon="el-icon-circle-plus-outline" :disabled="loading" @click="toggleExpand(true)">
              {{ $t(key('expand')) }}
            </el-button>
            <el-button icon="el-icon-remove-outline" :disabled="loading" @click="toggleExpand(false)">
              {{ $t(key('collapse')) }}
            </el-button>
          </el-button-group>
        </el-form-item>

        <el-form-item :label="$t(key('filter'))">
          <el-input
            v-model="filterText"
            :disabled="loading"
            :placeholder="$t(key('filter_placeholder'))"
            prefix-icon="el-icon-search"
            style="width: 240px;"
            clearable
          />
        </el-form-item>
      </el-form>

      <el-row :gutter="20">
        <el-col :span="10">
          <el-tree
            v-if="hackReset"
            ref="tree"
            class="tree-scroll"
            node-key="menu_id"
            :data="treeData"
            :props="treeProps"
            :filter-node-method="filterNode"
            highlight-current
            :default-expand-all="isExpandAll"
            :default-expanded-keys="expanded"
            draggable
            :allow-drag="allowDrag"
            @node-click="handleNodeClick"
            @node-drop="handleDrop"
            @node-expand="handleNodeExpand"
            @node-collapse="handleNodeCollapse"
          >
            <span class="custom-tree-node" slot-scope="{ node, data }">
              <span class="tree-label" :class="{ 'status-disabled': !data.status }">
                <i v-if="auth.move" class="el-icon-sort move-tree cs-mr-5" />
                <i v-if="data.icon" :class="'fa fa-' + data.icon" />
                <i v-else-if="data.children" :class="'el-icon-' + (node.expanded ? 'folder-opened' : 'folder')" />
                <i v-else class="el-icon-document" />
                {{ node.label }}
              </span>

              <span class="node-actions">
                <el-button
                  v-if="auth.create"
                  type="text"
                  size="mini"
                  @click.stop="handleAppend(data)"
                >
                  {{ $t(key('add')) }}
                </el-button>

                <el-button
                  v-if="auth.disabled_enable"
                  type="text"
                  size="mini"
                  @click.stop="handleToggleStatus(data)"
                >
                  {{ data.status ? $t(key('disable')) : $t(key('enable')) }}
                </el-button>

                <el-button
                  v-if="auth.delete"
                  type="text"
                  size="mini"
                  @click.stop="handleRemove(data.menu_id)"
                >
                  {{ $t(key('delete')) }}
                </el-button>
              </span>
            </span>
          </el-tree>
        </el-col>

        <el-col :span="14">
          <el-card v-show="auth.create || auth.edit" class="box-card" shadow="never">
            <div slot="header">
              <span>{{ $t(key(formStatus === 'create' ? 'add_menu' : 'edit_menu')) }}</span>
              <el-button
                v-if="formStatus === 'create' && auth.create"
                type="text"
                :loading="formLoading"
                style="float: right; padding: 3px 0;"
                @click="handleCreateSubmit"
              >
                {{ $t(key('confirm')) }}
              </el-button>
              <el-button
                v-else-if="formStatus === 'update' && auth.edit"
                type="text"
                :loading="formLoading"
                style="float: right; padding: 3px 0;"
                @click="handleUpdateSubmit"
              >
                {{ $t(key('modify')) }}
              </el-button>
            </div>

            <el-form :model="form" :rules="rules" ref="formRef" label-width="80px">
              <el-form-item :label="$t(key('parent_menu'))" prop="parent_id">
                <el-cascader
                  v-model="form.parent_id"
                  :placeholder="$t(key('parent_menu_placeholder'))"
                  :key="form.menu_id"
                  :options="treeData"
                  :props="cascaderProps"
                  style="width: 100%;"
                  filterable
                  clearable
                />
              </el-form-item>

              <el-row :gutter="20">
                <el-col :span="12">
                  <el-form-item :label="$t(key('menu_name'))" prop="name">
                    <el-input
                      v-model="form.name"
                      :placeholder="$t(key('menu_name_placeholder'))"
                      clearable
                    />
                  </el-form-item>
                </el-col>
                <el-col :span="12">
                  <el-form-item :label="$t(key('menu_alias'))" prop="alias">
                    <el-input
                      v-model="form.alias"
                      :placeholder="$t(key('menu_alias_placeholder'))"
                      clearable
                    />
                  </el-form-item>
                </el-col>
              </el-row>

              <el-row :gutter="20">
                <el-col :span="12">
                  <el-form-item :label="$t(key('icon'))" prop="icon">
                    <d2-icon-select
                      v-model="form.icon"
                      :user-input="true"
                      :placeholder="$t(key('select_menu_icon'))"
                    />
                  </el-form-item>
                </el-col>
                <el-col :span="12">
                  <el-form-item :label="$t(key('sort'))" prop="sort">
                    <el-input-number
                      v-model="form.sort"
                      :min="0"
                      :max="255"
                      style="width: 120px;"
                      controls-position="right"
                    />
                  </el-form-item>
                </el-col>
              </el-row>

              <el-row :gutter="20">
                <el-col :span="12">
                  <el-form-item :label="$t(key('navigation'))" prop="is_navi">
                    <el-switch v-model="form.is_navi" active-value="1" inactive-value="0" />
                  </el-form-item>
                </el-col>
              </el-row>

              <el-row :gutter="20">
                <el-col :span="12">
                  <el-form-item :label="$t(key('link_type'))" prop="type">
                    <el-radio-group v-model="form.type">
                      <el-radio :label="0">{{ $t(key('link_type_module')) }}</el-radio>
                      <el-radio :label="1">{{ $t(key('link_type_external')) }}</el-radio>
                    </el-radio-group>
                  </el-form-item>
                </el-col>
                <el-col :span="12">
                  <el-form-item :label="$t(key('open_type'))" prop="target">
                    <el-radio-group v-model="form.target">
                      <el-radio label="_self">{{ $t(key('open_self')) }}</el-radio>
                      <el-radio label="_blank">{{ $t(key('open_blank')) }}</el-radio>
                    </el-radio-group>
                  </el-form-item>
                </el-col>
              </el-row>

              <el-form-item label="URL" prop="url">
                <el-input
                  v-model="form.url"
                  :placeholder="$t(key('url_placeholder'))"
                  clearable
                />
              </el-form-item>

              <el-form-item :label="$t(key('params'))" prop="params">
                <el-input
                  v-model="form.params"
                  :placeholder="$t(key('params_placeholder'))"
                  clearable
                />
              </el-form-item>

              <el-form-item :label="$t(key('remark'))" prop="remark">
                <el-input
                  v-model="form.remark"
                  :placeholder="$t(key('remark_placeholder'))"
                  type="textarea"
                  :rows="3"
                />
              </el-form-item>
            </el-form>
          </el-card>
        </el-col>
      </el-row>
    </div>
  </d2-container>
</template>

<script>
import util from '@/libs/util'
import { i18nMixin } from '@/composables/useI18n'
import { confirmMixin } from '@/composables/useConfirmHandle'
import {
  getMenuList,
  createMenu,
  editMenu,
  deleteMenu,
  updateMenuStatus,
  sortMenu
} from '@/api/system-administration/menu-configuration'

export default {
  name: 'system-administration-menu-configuration',
  mixins: [i18nMixin('page.system_administration.menu_management.menu_configuration'), confirmMixin],
  data () {
    const $t = this.$t.bind(this)
    return {
      loading: false,
      formLoading: false,
      treeData: [],
      module: 'admin',
      hackReset: true,
      isExpandAll: false,
      expanded: [],
      oldExpanded: [],
      filterText: '',
      formStatus: 'create',
      appendData: {},
      appendType: '',
      updateNode: {},
      auth: {
        create: true,
        delete: true,
        edit: true,
        disabled_enable: true,
        move: true
      },
      treeProps: {
        label: 'name',
        children: 'children'
      },
      cascaderProps: {
        value: 'menu_id',
        label: 'name',
        children: 'children',
        checkStrictly: true,
        emitPath: false
      },
      searchForm: {
        module: 'admin',
        status: undefined,
        is_navi: undefined,
        level: 0
      },
      form: this.getDefaultForm(),
      rules: {
        name: [
          { required: true, message: $t(this.key('name_required')), trigger: 'blur' },
          { max: 32, message: $t(this.key('name_max_length')), trigger: 'blur' }
        ],
        alias: [
          { max: 16, message: $t(this.key('alias_max_length')), trigger: 'blur' }
        ],
        sort: [
          { type: 'number', message: $t(this.key('must_be_number')), trigger: 'blur' }
        ],
        url: [
          { max: 255, message: $t(this.key('max_255_length')), trigger: 'blur' }
        ],
        params: [
          { max: 255, message: $t(this.key('max_255_length')), trigger: 'blur' }
        ],
        remark: [
          { max: 255, message: $t(this.key('max_255_length')), trigger: 'blur' }
        ]
      }
    }
  },
  watch: {
    filterText (val) {
      this.$refs.tree && this.$refs.tree.filter(val)
      this.expanded = this.oldExpanded
    }
  },
  mounted () {
    this._validationAuth()
    this.handleSearch()
  },
  methods: {
    getDefaultForm () {
      return {
        parent_id: 0,
        name: '',
        alias: '',
        icon: '',
        remark: '',
        type: 0,
        url: '',
        params: '',
        target: '_self',
        is_navi: '0',
        sort: 50
      }
    },
    _validationAuth () {
      this.auth.create = this.$permission('/system_settings/menu_configuration/menu/create')
      this.auth.edit = this.$permission('/system_settings/menu_configuration/menu/edit')
      this.auth.delete = this.$permission('/system_settings/menu_configuration/menu/delete')
      this.auth.disabled_enable = this.$permission('/system_settings/menu_configuration/menu/disabled_enable')
      this.auth.move = this.$permission('/system_settings/menu_configuration/menu/sort')
    },
    filterNode (value, data) {
      if (!value) return true
      return data.name.indexOf(value) !== -1
    },
    toggleExpand (isExpand) {
      this.filterText = ''
      this.expanded = []
      this.hackReset = false
      this.$nextTick(() => {
        this.isExpandAll = isExpand
        this.hackReset = true
      })
    },
    resetForm () {
      this.form = this.getDefaultForm()
    },
    resetElements (status = 'create') {
      this.$nextTick(() => {
        this.$refs.formRef && this.$refs.formRef.clearValidate()
      })
      this.formStatus = status
      this.formLoading = false
    },
    handleSearch () {
      const form = { ...this.searchForm }
      form.level = form.level <= 0 ? undefined : form.level - 1
      this.loading = true
      getMenuList(form)
        .then(res => {
          this.module = form.module
          const menuKey = 'menu_' + form.module
          const flatList = Array.isArray(res)
            ? res
            : (res[menuKey] || (res.data && res.data[menuKey]) || [])
          this.treeData = util.formatDataToTree(flatList)
          this.filterText = ''
          this.resetForm()
          this.resetElements('create')
          if (this.$refs.tree && this.$refs.tree.getCurrentKey()) {
            this.$refs.tree.setCurrentKey(null)
          }
        })
        .finally(() => {
          this.loading = false
        })
    },
    handleReset () {
      this.$refs.searchFormRef.resetFields()
      this.searchForm.module = 'admin'
      this.searchForm.level = 0
      this.handleSearch()
    },
    handleNodeClick (data) {
      if (!this.auth.create && !this.auth.edit) return
      this.updateNode = data
      this.resetForm()
      this.resetElements('update')
      this.form = { ...data, is_navi: String(data.is_navi) }
    },
    handleCreateTop () {
      this.appendType = 'newTopMenu'
      this.resetForm()
      this.resetElements('create')
      if (this.$refs.tree && this.$refs.tree.getCurrentKey()) {
        this.$refs.tree.setCurrentKey(null)
      }
    },
    handleAppend (data) {
      const key = data.menu_id
      this.handleCreateTop()
      this.$refs.tree.setCurrentKey(key)
      this.form.parent_id = key
      this.appendType = 'newChildMenu'
      this.appendData = data
    },
    appendChildToTree (newNode) {
      if (!this.appendData.children) {
        this.$set(this.appendData, 'children', [])
      }
      this.appendData.children.push(newNode)
    },
    appendTopToTree (newNode) {
      if (!newNode.children) {
        this.$set(newNode, 'children', [])
      }
      this.treeData.push(newNode)
    },
    updateNodeInTree (data) {
      if (!this.updateNode) return
      Object.keys(data).forEach(key => {
        if (Object.prototype.hasOwnProperty.call(this.updateNode, key)) {
          this.updateNode[key] = data[key]
        }
      })
      this.updateNode = {}
    },
    handleCreateSubmit () {
      this.$refs.formRef.validate(valid => {
        if (!valid) return
        this.oldExpanded = this.expanded
        this.formLoading = true
        createMenu({ ...this.form, module: this.module })
          .then(res => {
            let data = res
            if (data && data.data) data = data.data
            if (Array.isArray(data) && data.length > 0) data = data[0]

            if (!this.isExpandAll) {
              this.expanded = [data.parent_id || data.menu_id]
            }

            if (this.appendType === 'newChildMenu') {
              this.appendChildToTree(data)
            } else if (this.appendType === 'newTopMenu') {
              this.appendTopToTree(data)
            }
            this.appendType = ''
            this.$message.success(this.$t(this.key('operation_success')))
          })
          .finally(() => {
            this.formLoading = false
          })
      })
    },
    handleUpdateSubmit () {
      this.$refs.formRef.validate(valid => {
        if (!valid) return
        this.oldExpanded = this.expanded
        this.formLoading = true
        editMenu(this.form)
          .then(() => {
            this.updateNodeInTree(this.form)
            this.$message.success(this.$t(this.key('operation_success')))
          })
          .finally(() => {
            this.formLoading = false
          })
      })
    },
    async handleRemove (key) {
      const cancelled = await this.$confirmAction(
        {
          message: this.key('delete_confirm'),
          title: this.key('prompt'),
          confirmButtonText: this.key('confirm'),
          cancelButtonText: this.key('cancel')
        },
        () => deleteMenu({ menu_id: key })
      )
      if (cancelled) return
      this.$refs.tree.remove(this.$refs.tree.getNode(key))
      this.$message.success(this.$t(this.key('operation_success')))
    },
    async handleToggleStatus (data) {
      const cancelled = await this.$confirmAction(
        {
          message: this.key('status_change_confirm'),
          title: this.key('prompt'),
          confirmButtonText: this.key('confirm'),
          cancelButtonText: this.key('cancel')
        },
        () => updateMenuStatus({ menu_id: data.menu_id, status: data.status ? 0 : 1 })
      )
      if (cancelled) return
      if (!this.isExpandAll) {
        const node = this.$refs.tree.getNode(data.menu_id)
        this.expanded = [node && node.data ? (node.data.parent_id || data.menu_id) : data.menu_id]
      }
      this.handleSearch()
    },
    handleDrop (draggingNode, dropNode, dropType) {
      const setMenu = {
        menu_id: draggingNode.data.menu_id,
        parent_id: draggingNode.data.parent_id
      }
      const indexMenu = []

      if (dropType === 'inner') {
        setMenu.parent_id = dropNode.key
      } else {
        setMenu.parent_id = dropNode.data.parent_id
        dropNode.parent.childNodes.forEach((value, index) => {
          indexMenu.push(value.key)
          value.data.sort = index + 1
        })
      }

      if (indexMenu.length > 0) {
        sortMenu({ menu_sort: indexMenu, ...setMenu })
          .finally(() => { this.handleSearch() })
          .catch(() => { this.handleSearch() })
      }
    },
    allowDrag () {
      return true
    },
    handleNodeExpand (data) {
      const exists = this.expanded.some(item => item === data.menu_id)
      if (!exists) {
        this.expanded.push(data.menu_id)
      }
    },
    handleNodeCollapse (data) {
      this.expanded = this.expanded.filter(item => item !== data.menu_id)
      this._removeChildrenIds(data)
    },
    _removeChildrenIds (data) {
      if (!data.children) return
      data.children.forEach(item => {
        const index = this.expanded.indexOf(item.menu_id)
        if (index >= 0) {
          this.expanded.splice(index, 1)
        }
        this._removeChildrenIds(item)
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.tree-scroll {
  max-height: 615px;
  overflow: auto;
  padding-bottom: 1px;
}

.custom-tree-node {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 14px;
  padding-right: 8px;
}

.tree-label {
  i {
    width: 16px;
  }
  .fa {
    font-size: 16px;
    vertical-align: baseline;
  }
}

.node-actions {
  display: none;
}

.custom-tree-node:hover .node-actions {
  display: block;
}

.move-tree {
  color: #c0c4cc;
  cursor: move;
}

.status-disabled {
  color: #c0c4cc;
  text-decoration: line-through;
}

.box-card {
  border-radius: 0;
  border: 1px solid #dcdfe6;
}
</style>
