<template>
  <div class="page-view flex-row-sb">
    <app-sync-tree
      ref="appAsyncTree"
      :actions="treeActions"
      :default-data="{ customConfigField: 'custom config field' }"
      :default-expanded-keys="defaultExpandedKeys"
      :expand-all="expandAll"
      :fields="treeFields"
      :props="treeProps"
      :request-method="getTreeNodes"
      highlight-first
      title="多维字典目录"
      @select="onTreeNodeClick"
    >
      <template #form="{ form: treeForm }">
        <app-filter-item :span="24" label="父级">
          <el-input v-model="curNode.name" disabled readonly />
        </app-filter-item>
        <app-filter-item :rules="[{ required: true, message: '分类名称不能为空' }]" :span="24" label="分类名称">
          <el-input v-model="treeForm.name" placeholder="请输入分类名称" />
        </app-filter-item>
        <app-filter-item :rules="[{ required: true, message: '分类编码不能为空' }]" :span="24" label="分类编码">
          <el-input v-model="treeForm.typeKey" :disabled="treeForm.id ? true : false" placeholder="请输入分类编码" />
        </app-filter-item>
      </template>
    </app-sync-tree>

    <div class="page-view flex-1 ml-15 h-100">
      <app-filter ref="app-filter" :model="filters" class="bg-white px-15 border-radius-5" label-width="90px"
                  @query="onQuery" @reset="onReset"
      >
        <app-filter-item :filter-config="{ operation: 'EQUAL' }" :span="6" el-type="input" label="字典项名称">
          <el-input v-model="filters.name" clearable placeholder="请输入"
                    @keyup.enter.native="$refs['app-filter'].query"
          />
        </app-filter-item>
        <app-filter-item :filter-config="{ operation: 'EQUAL' }" :span="6" el-type="input" label="字典项编码">
          <el-input v-model="filters.key" clearable placeholder="请输入"
                    @keyup.enter.native="$refs['app-filter'].query"
          />
        </app-filter-item>
      </app-filter>

      <div class="pa-11 bg-white h-100 flex-col mt-11 border-radius-5">
        <div class="mb-11">
          <el-button icon="el-icon-plus" size="mini" type="primary" @click="addSubNode"> 新增</el-button>
          <el-popover v-model="uploadVisible" placement="bottom" trigger="hover">
            <p>请先下载模板，填写完成后上传</p>
            <div class="text-c flex-row-c">
              <el-button class="mr-10" plain size="mini" type="primary" @click="download">下载模板</el-button>
              <el-upload
                ref="upload"
                :action="actionUrl"
                :before-upload="beforeUpload"
                :headers="headers"
                :multiple="false"
                :on-error="handleError"
                :on-success="handleSuccess"
                :show-file-list="false"
              >
                <el-button plain size="mini" type="warning">导入字典项</el-button>
              </el-upload>
            </div>
            <el-button slot="reference" :loading="uploadLoading" class="ml-10" icon="el-icon-upload2" size="mini"
                       type="primary"
            > 导入
            </el-button>
          </el-popover>
        </div>
        <app-table
          ref="table"
          :column="columns"
          :pagination="pagination"
          :table-data="tableData"
          :table-loading="tableLoading"
          :total="total"
          row-key="id"
          @pagination-change="onPaginationChange"
          @row-dblclick="editSubNode"
        />
      </div>
    </div>
    <sub-dimension-dialog v-model="dialogVisible" :data="currentRow" :title="dialogTitle" @confirm="onDialogConfirm" />
    <sub-tree-drawer v-model="drawerVisible" :data="parentRow" :title="drawerTitle" />
  </div>
</template>

<script>
import utils from '@/utils';
import { getDictionaryByCode } from '@/utils/dictionary.js';
import tableMixin from '@/mixins/table.js'; // 列表mixins
import SubDimensionDialog from './SubDimensionDialog.vue';
import SubTreeDrawer from './SubTreeDrawer.vue';
import API from '@/api/business/dict.js';
import { mapState } from 'vuex';

export default {
  name: 'DataDimension',
  components: { SubDimensionDialog, SubTreeDrawer },
  mixins: [tableMixin],
  data() {
    const TREE_CONFIG = {
      treeProps: {
        value: 'id',
        label: 'name',
      },
      expandAll: false,
      defaultExpandedKeys: ['5'],
      // 此处fields内配置对象属性包括: label/prop/rules/type(枚举:switch/number/input(默认))/span/
      treeFields: [],
      curNode: {}, // 当前选择节点
    };
    const TABLE_CONFIG = {
      // 列表表头
      columns: [
        { label: '字典项名称', prop: 'name', align: 'left' },
        { label: '字典项编码', prop: 'key' },
        {
          dataType: 'option',
          label: '操作',
          width: '200px',
          fixed: 'right',
          operation: [
            {
              name: '管理字典项',
              type: 'text',
              icon: 'el-icon-setting',
              size: 'mini',
              color: '#409eff',
              plain: true,
              clickFun: (row) => {
                this.subDictTree(row);
              },
            },
            {
              name: '删除',
              type: 'text',
              size: 'mini',
              icon: 'el-icon-delete',
              color: 'red',
              plain: true,
              showHide: (row) => {
                return row.id !== 1;
              },
              clickFun: (row) => {
                this.delSubNode(row);
              },
            },
          ],
        },
      ],
      defaultFilter: [
        {
          operation: 'EQUAL',
          property: 'typeId',
          relation: 'AND',
          value: '',
        },
        {
          operation: 'EQUAL',
          property: 'type',
          relation: 'AND',
          value: 'dic',
        },
      ],
      rowData: {},
    };
    const DIALOG_CONFIG = {
      dialogVisible: false,
      dialogTitle: '新增',
      fieldDialogVisible: false,
      dialogdData: {},
      drawerVisible: false,
      drawerTitle: '',
      parentRow: {},
    };

    return {
      treeData: [],
      ...TREE_CONFIG,
      ...TABLE_CONFIG,
      ...DIALOG_CONFIG,
      uploadVisible: false,
      uploadLoading: false,
    };
  },
  computed: {
    // 配置列表接口方法：只有配置了该计算属性 才会执行 table mixins中的getData方法
    dataMethod() {
      return API.getMultiDict;
    },
    actionUrl: (_) => `${window.context.portal}/sys/dataDict/v1/importDic`,
    ...mapState({
      headers: (state) => ({
        Authorization: `Bearer ${state.login.currentUser.token}`,
      }),
    }),
  },
  methods: {
    getDictionaryByCode,
    // 树 -----------------
    async getTreeNodes() {
      const value = await API.getMultiDictTree();
      const temp = value.map((item) => {
        delete item.icon;
        return item;
      });
      const treeData = utils.tile2nest(temp);
      this.defaultExpandedKeys = [treeData?.[0].id] || [];
      return { data: treeData[0] || [] };
    },

    reloadTree() {
      this.$refs.appAsyncTree.getTreeData();
    },

    /**
     * @description: treeActions支持函数也支持数组，由于实际场景当中 可能会动态设置按钮，故以函数示例来展示
     * @param {*} data 当前点击节点数据
     * @return {*} 当前节点展示的操作项数组
     */
    treeActions(data) {
      // 根节点判断方式以业务为准，此处仅为示例
      return [
        { label: '添加', type: 'add', method: this.addNode },
        { label: '编辑', type: 'edit', method: this.editNode },
        { label: '删除', type: 'delete', method: this.delNode, tips: '及其子节点' },
      ];
    },
    onTreeNodeClick(node) {
      if (node.id === this.curNode?.id) return;
      this.curNode = { ...node };
      this.getData();
    },
    async addNode(params, node) {
      const { typeKey, name } = params;
      const { id } = node;
      const newNode = {
        parentId: id,
        sysType: {
          children: [],
          icon: '{}',
          id: '',
          isLeaf: 'N',
          name,
          parentId: id,
          struType: 0,
          typeGroupKey: node.parentId == 0 ? node.typeKey : node.typeGroupKey,
          typeKey,
        },
      };
      try {
        const { state, message } = await API.saveMultiDirNode(newNode);
        if (state) {
          this.reloadTree();
        } else {
          this.$message.warning(message || '添加字典分类失败！');
        }
      } catch (err) {
        this.$message.warning(err || '添加字典分类失败！');
      }
    },
    async editNode(params) {
      const { parentId } = params;

      const newNode = {
        parentId: parentId,
        sysType: params,
      };
      try {
        const { state, message } = await API.saveMultiDirNode(newNode);
        if (state) {
          this.reloadTree();
        } else {
          this.$message.warning(message || '编辑字典分类失败！');
        }
      } catch (err) {
        this.$message.warning(err || '编辑字典分类失败！');
      }
    },
    async delNode(params, node) {
      try {
        const { state, message } = await API.removeMultiDirNode(node);
        if (state) {
          this.reloadTree();
          //   this.$message.success('删除字典分类成功！');
        } else {
          this.$message.warning(message || '删除字典分类失败！');
        }
      } catch (err) {
        this.$message.warning(err || '删除字典分类失败！');
      }
    },
    // 列表 -----------------
    onBeforeGetData(queryParams, formData) {
      this.setFilterValue(this.queryParams, this.defaultFilter, 'typeId', this.curNode.id);
      this.setFilterValue(this.queryParams, this.defaultFilter, 'type', 'dic');
    },
    addSubNode() {
      this.dialogTitle = '新增字典项';
      this.currentRow = {
        key: '',
        name: '',
        parentId: this.curNode.id,
        typeId: this.curNode.id,
      };
      this.dialogVisible = true;
    },
    editSubNode(row) {
      this.dialogTitle = `编辑 - ${row.name}`;
      this.currentRow = row;
      this.dialogVisible = true;
    },
    delSubNode(row) {
      this.$confirm(`此操作将删除字典项（${row.name}）及其子节点, 是否继续?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(async () => {
          try {
            const { state, message } = await API.removeDictNode(row.id);
            if (state) {
              this.$message.success('删除字典项成功');
              this.getData();
            } else {
              this.$message.warning(message || '删除字典项失败！');
            }
          } catch (err) {
            this.$message.warning(err || '删除字典项失败！');
          }
        })
        .catch(() => {
        });
    },
    onDialogConfirm() {
      this.getData();
    },

    // 下级字典树
    subDictTree(row) {
      this.drawerTitle = `多维字典 - ${row.name}`;
      this.parentRow = row;
      this.drawerVisible = true;
    },

    download() {
      const fileType = '多维字典定义表模版.xlsx';
      var a = document.createElement('a');
      a.href = window.context.download + fileType;
      a.download = fileType;
      a.style.display = 'none';
      document.body.appendChild(a);
      a.click();
      a.remove();
    },

    //  导入文件上传前
    beforeUpload(file) {
      return new Promise((resolve, reject) => {
        this.uploadLoading = true;
        const { size, name } = file;
        this.$confirm(`请确认是否导入 “${name}” ？`, '提示', {
          confirmButtonText: '确认',
          cancelButtonText: '取消',
          type: 'warning',
        })
          .then((_) => {
            let passedOrNot = true; // 是否通过验证
            const typeRegular = /\.(xls|xlsx)$/i;
            const verification = [
              {
                condition: !typeRegular.test(name),
                result: () => this.$message.error('只能上传xls或xlsx文件类型'),
              },
            ];
            verification.forEach((item) => {
              if (item.condition) {
                item.result();
                passedOrNot = false;
                this.uploadLoading = false;
              }
            });

            passedOrNot ? resolve() : reject();
          })
          .catch((_) => {
            reject();
            this.uploadLoading = false;
            this.$message.warning('已取消上传');
          });
      });
    },

    // 导入文件上传成功
    handleSuccess(res) {
      if (res.state) {
        this.$message.success('导入成功');
        this.uploadLoading = false;
        this.getData();
      } else {
        this.$message.error(res.message);
      }
    },

    // 导入文件上传失败
    handleError(err) {
      if (err) {
        let errData = err.toString().replace('Error:', '');
        errData = JSON.parse(errData);
        this.$message.error(errData.message || '导入失败，请重试');
      } else {
        this.$message.error('导入失败，请重试');
      }
      this.uploadLoading = false;
    },
  },
};
</script>

<style lang="scss" scoped></style>
