<template>
  <basic-container>
    <avue-crud
      :option="option"
      :table-loading="loading"
      :data="data"
      v-model:page="page"
      :permission="permissionList"
      :before-open="beforeOpen"
      v-model="form"
      ref="crud"
      @row-update="rowUpdate"
      @row-save="rowSave"
      @row-del="rowDel"
      @search-change="searchChange"
      @search-reset="searchReset"
      @selection-change="selectionChange"
      @current-change="currentChange"
      @size-change="sizeChange"
      @refresh-change="refreshChange"
      @on-load="onLoad"
    >
      <template #menu-left>
        <el-button
          type="danger"
          icon="el-icon-delete"
          plain
          v-if="permission.topmenu_delete"
          @click="handleDelete"
          >删除
        </el-button>
        <el-button
          icon="el-icon-setting"
          @click="handleMenuSetting"
          v-if="permission.topmenu_setting"
          plain
          >菜单配置
        </el-button>
      </template>
      <template #menu="scope">
        <el-link
          v-if="permission.topmenu_setting"
          type="primary"
          @click.stop="handleRowMenuSetting(scope.row)"
          >配置
        </el-link>
      </template>
      <template #name="{ row }">
        <i :class="row.source" style="margin-right: 5px" />
        <span>{{ row.name }}</span>
      </template>
      <template #source="{ row }">
        <div style="text-align: center">
          <i :class="row.source"></i>
        </div>
      </template>
      <template #sort="{ row }">
        <el-input-number
          v-model="row.sort"
          @change="sortChange(row)"
          :min="1"
          :max="100"
        ></el-input-number>
      </template>
      <template #isMain="{ row }">
        <el-switch
          v-model="row.isMain"
          inline-prompt
          :before-change="() => isMainChange(row)"
          active-text="是"
          inactive-text="否"
          :active-value="1"
          :inactive-value="0"
        />
      </template>
    </avue-crud>
    <el-dialog
      title="下级菜单配置"
      append-to-body
      v-model="box"
      width="345px"
      @closed="handleDialogClose"
    >
      <el-row
        justify="space-between"
        align="middle"
        style="margin-bottom: 12px; background: #f5f7fa; padding: 6px 10px; border-radius: 4px"
      >
        <span style="display: inline-flex; align-items: center">
          <el-switch
            v-model="menuLinked"
            active-text="节点联动"
            size="small"
            @change="handleLinkedChange"
          />
          <el-tooltip content="开启后勾选父节点会自动勾选所有子节点，关闭则可独立勾选任意节点" placement="top">
            <el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
          </el-tooltip>
        </span>
        <el-button-group>
          <el-button size="small" plain @click="handleSelectAll">全选</el-button>
          <el-button size="small" plain @click="handleInvertSelect">反选</el-button>
        </el-button-group>
      </el-row>
      <el-tree
        :data="menuGrantList"
        show-checkbox
        :check-strictly="!menuLinked"
        node-key="id"
        ref="treeMenu"
        :default-checked-keys="menuTreeObj"
        :props="props"
      >
      </el-tree>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="box = false">取 消</el-button>
          <el-button type="primary" @click="submit">确 定</el-button>
        </span>
      </template>
    </el-dialog>
  </basic-container>
</template>

<script>
import {
  getList,
  getDetail,
  add,
  update,
  remove,
  grant,
  grantTree,
  getTopTree,
  enable,
} from '@/api/system/topmenu';
import { mapGetters } from 'vuex';
import iconList from '@/config/iconList';

export default {
  data() {
    return {
      form: {},
      box: false,
      query: {},
      loading: true,
      currentMenuIds: [],
      props: {
        label: 'title',
        value: 'key',
      },
      page: {
        pageSize: 10,
        currentPage: 1,
        total: 0,
      },
      selectionList: [],
      menuGrantList: [],
      menuTreeObj: [],
      menuLinked: false,
      option: {
        height: 'auto',
        calcHeight: 32,
        tip: false,
        searchShow: true,
        searchMenuSpan: 24,
        searchIcon: true,
        searchIndex: 8,
        searchMenuPosition: 'right',
        border: true,
        index: true,
        viewBtn: true,
        selection: true,
        menuWidth: 180,
        dialogWidth: 900,
        dialogClickModal: false,
        column: [
          {
            label: '菜单名',
            prop: 'name',
            search: true,
            rules: [
              {
                required: true,
                message: '请输入菜单名',
                trigger: 'blur',
              },
            ],
          },
          {
            label: '菜单图标',
            prop: 'source',
            type: 'icon',
            slot: true,
            iconList: iconList,
            rules: [
              {
                required: true,
                message: '请输入菜单图标',
                trigger: 'click',
              },
            ],
          },
          {
            label: '菜单编号',
            prop: 'code',
            search: true,
            rules: [
              {
                required: true,
                message: '请输入菜单编号',
                trigger: 'blur',
              },
            ],
          },
          {
            label: '菜单排序',
            prop: 'sort',
            type: 'number',
            slot: true,
            rules: [
              {
                required: true,
                message: '请输入菜单排序',
                trigger: 'blur',
              },
            ],
          },
          {
            label: '是否主页',
            prop: 'isMain',
            slot: true,
            align: 'center',
            width: 100,
            value: 0,
            display: false,
            dicData: [
              { label: '否', value: 0 },
              { label: '是', value: 1 },
            ],
          },
          {
            label: '菜单路由',
            prop: 'path',
            span: 24,
            hide: true,
            rules: [
              {
                required: false,
                message: '请输入菜单路由',
                trigger: 'blur',
              },
            ],
          },
        ],
      },
      data: [],
    };
  },
  computed: {
    ...mapGetters(['permission', 'userInfo']),
    permissionList() {
      return {
        addBtn: this.validData(this.permission.topmenu_add, false),
        viewBtn: this.validData(this.permission.topmenu_view, false),
        delBtn: this.validData(this.permission.topmenu_delete, false),
        editBtn: this.validData(this.permission.topmenu_edit, false),
      };
    },
    ids() {
      let ids = [];
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids.join(',');
    },
    idsArray() {
      let ids = [];
      this.selectionList.forEach(ele => {
        ids.push(ele.id);
      });
      return ids;
    },
  },
  methods: {
    getAllNodeKeys(nodes) {
      let keys = [];
      nodes.forEach(node => {
        keys.push(node.id);
        if (node.children && node.children.length > 0) {
          keys = keys.concat(this.getAllNodeKeys(node.children));
        }
      });
      return keys;
    },
    getLeafKeys(nodes) {
      let keys = [];
      nodes.forEach(node => {
        if (!node.children || node.children.length === 0) {
          keys.push(node.id);
        } else {
          keys = keys.concat(this.getLeafKeys(node.children));
        }
      });
      return keys;
    },
    handleSelectAll() {
      const tree = this.$refs.treeMenu;
      if (!tree) return;
      const allKeys = this.getAllNodeKeys(this.menuGrantList);
      tree.setCheckedKeys(allKeys);
    },
    handleInvertSelect() {
      const tree = this.$refs.treeMenu;
      if (!tree) return;
      const checkedKeys = new Set(tree.getCheckedKeys());
      if (this.menuLinked) {
        const leafKeys = this.getLeafKeys(this.menuGrantList);
        const invertedKeys = leafKeys.filter(key => !checkedKeys.has(key));
        tree.setCheckedKeys(invertedKeys);
      } else {
        const allKeys = this.getAllNodeKeys(this.menuGrantList);
        const invertedKeys = allKeys.filter(key => !checkedKeys.has(key));
        tree.setCheckedKeys(invertedKeys);
      }
    },
    handleLinkedChange() {
      const tree = this.$refs.treeMenu;
      if (!tree) return;
      const checkedKeys = tree.getCheckedKeys();
      const halfCheckedKeys = tree.getHalfCheckedKeys();
      this.$nextTick(() => {
        tree.setCheckedKeys([...checkedKeys, ...halfCheckedKeys]);
      });
    },
    // el-tree 会把 setCheckedKeys 的入参记录为默认勾选集并在树数据重载时回放，
    // 复用的树实例需在打开配置时整体覆盖勾选状态，避免残留上一次配置的勾选
    applyCheckedKeys() {
      const tree = this.$refs.treeMenu;
      if (!tree) return;
      tree.setCheckedKeys(this.menuTreeObj);
    },
    isMainChange(row) {
      const newValue = row.isMain === 1 ? 0 : 1;
      const request = newValue === 1 ? enable(row.id) : update({ ...row, isMain: newValue });
      return request.then(() => {
        this.onLoad(this.page);
        this.$message({ type: 'success', message: '操作成功!' });
        return true;
      }).catch(error => {
        window.console.log(error);
        return false;
      });
    },
    submit() {
      const menuList = this.$refs.treeMenu.getCheckedKeys();
      grant(this.currentMenuIds, menuList).then(() => {
        this.box = false;
        this.$message({
          type: 'success',
          message: '操作成功!',
        });
        this.onLoad(this.page);
      });
    },
    rowSave(row, done, loading) {
      add(row).then(
        () => {
          this.onLoad(this.page);
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
        },
        error => {
          window.console.log(error);
          loading();
        }
      );
    },
    rowUpdate(row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page);
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          done();
        },
        error => {
          window.console.log(error);
          loading();
        }
      );
    },
    rowDel(row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id);
        })
        .then(() => {
          this.onLoad(this.page);
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
        });
    },
    handleDelete() {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据');
        return;
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids);
        })
        .then(() => {
          this.onLoad(this.page);
          this.$message({
            type: 'success',
            message: '操作成功!',
          });
          this.$refs.crud.toggleSelection();
        });
    },
    handleMenuSetting() {
      if (this.selectionList.length !== 1) {
        this.$message.warning('只能选择一条数据');
        return;
      }
      this.currentMenuIds = this.idsArray;
      this.menuTreeObj = [];
      grantTree().then(res => {
        this.menuGrantList = res.data.data.menu;
        getTopTree(this.ids).then(res => {
          this.menuTreeObj = res.data.data.menu;
          this.box = true;
          this.$nextTick(() => this.applyCheckedKeys());
        });
      });
    },
    handleRowMenuSetting(row) {
      this.currentMenuIds = [row.id];
      this.menuTreeObj = [];
      grantTree().then(res => {
        this.menuGrantList = res.data.data.menu;
        getTopTree(row.id).then(res => {
          this.menuTreeObj = res.data.data.menu;
          this.box = true;
          this.$nextTick(() => this.applyCheckedKeys());
        });
      });
    },
    handleDialogClose() {
      this.currentMenuIds = [];
      this.menuLinked = false;
    },
    beforeOpen(done, type) {
      if (['edit', 'view'].includes(type)) {
        getDetail(this.form.id).then(res => {
          this.form = res.data.data;
        });
      }
      done();
    },
    sortChange(row) {
      update(row).then(
        () => {
          this.onLoad(this.page);
        },
        error => {
          window.console.log(error);
        }
      );
    },
    searchReset() {
      this.query = {};
      this.onLoad(this.page);
    },
    searchChange(params, done) {
      this.query = params;
      this.page.currentPage = 1;
      this.onLoad(this.page, params);
      done();
    },
    selectionChange(list) {
      this.selectionList = list;
    },
    selectionClear() {
      this.selectionList = [];
      this.$refs.crud.toggleSelection();
    },
    currentChange(currentPage) {
      this.page.currentPage = currentPage;
    },
    sizeChange(pageSize) {
      this.page.pageSize = pageSize;
    },
    refreshChange() {
      this.onLoad(this.page, this.query);
    },
    onLoad(page, params = {}) {
      this.loading = true;
      getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
        const data = res.data.data;
        this.page.total = data.total;
        this.data = data.records;
        this.loading = false;
        this.selectionClear();
      });
    },
  },
};
</script>

<style>
.none-border {
  border: 0;
  background-color: transparent !important;
}
</style>
