<route lang="yaml">
meta:
  enabled: false
</route>

<script lang="ts" setup name="AppRolePermit">
import { ElMessage, ElMessageBox } from 'element-plus'
import type Node from 'element-plus/es/components/tree/src/model/node'
import dictionaryDialog from './components/dictionaryDialog/index.vue'
import dictionaryItemDialog from './components/dictionaryItemDialog/index.vue'
import api from '@/api'

interface Dict {
  id: string | number
  label: string
  code: string
  children?: Dict[]
}
const dictionaryRef = ref()
const dictionary = ref({
  search: '',
  tree: [] as Dict[],
  currentNode: undefined as Node | undefined,
  currentData: undefined as Dict | undefined,
  dialog: {
    visible: false,
    parentId: '' as Dict['id'],
    id: '' as Dict['id'],
  },
})

const { pagination, getParams, onSizeChange, onCurrentChange, onSortChange } = usePagination()
pagination.value.size = 20
pagination.value.sizes = [20, 50, 100]
const dictionaryItemRef = ref()
const dictionaryItem = ref({
  loading: false,
  // 搜索
  search: {
    dictionaryId: '' as Dict['id'],
    title: '',
  },
  // 列表数据
  dataList: [],
  selectionDataList: [],
  dialog: {
    visible: false,
    id: '' as string | number,
  },
})

const getDictionaryList = () => {
  dictionaryItem.value.search.dictionaryId = ''
  api.get('pages_example/dictionary/list', {
    baseURL: '/mock/',
  }).then((res) => {
    dictionary.value.tree = res.data
  })
}
onMounted(() => {
  getDictionaryList()
})
watch(() => dictionary.value.search, (val) => {
  dictionaryRef.value!.filter(val)
})
const dictionaryFilter = (value: string, data: Dict) => {
  if (!value) {
    return true
  }
  return data.label.includes(value)
}
const dictionaryAdd = (data?: Dict) => {
  dictionary.value.currentData = data
  dictionary.value.dialog.parentId = data?.id ?? ''
  dictionary.value.dialog.id = ''
  dictionary.value.dialog.visible = true
}
const dictionaryEdit = (node: Node, data: Dict) => {
  dictionary.value.currentNode = node
  dictionary.value.currentData = data
  dictionary.value.dialog.parentId = node.parent.data.id ?? ''
  dictionary.value.dialog.id = data.id
  dictionary.value.dialog.visible = true
}
const dictionaryDelete = (node: Node, data: Dict) => {
  ElMessageBox.confirm(`确认删除「${data.label}」吗？`, '确认信息').then(() => {
    api.post('pages_example/dictionary/delete', {
      id: data.id,
    }, {
      baseURL: '/mock/',
    }).then(() => {
      ElMessage.success({
        message: '模拟删除成功',
        center: true,
      })
      const parent = node.parent
      const children: Dict[] = parent.data.children || parent.data
      const index = children.findIndex(d => d.id === data.id)
      children.splice(index, 1)
      dictionary.value.tree = [...dictionary.value.tree]
    })
  })
}
// 新增成功后更新树
const dictionaryAddNode = (data: Dict) => {
  if (dictionary.value.currentData) {
    if (!dictionary.value.currentData.children) {
      dictionary.value.currentData.children = []
    }
    dictionary.value.currentData.children.push({
      id: data.id,
      label: data.label,
      code: data.code,
    })
  }
  else {
    dictionary.value.tree.push({
      id: data.id,
      label: data.label,
      code: data.code,
    })
  }
}
// 编辑成功后更新树
const dictionaryEditNode = (data: Dict, parentId: string | number) => {
  if (dictionary.value.currentNode && dictionary.value.currentData) {
    if ((dictionary.value.currentNode.parent.data.id ?? '') === parentId) {
      // 如果 parentId 一致说明节点位置没有变化，直接更新
      dictionary.value.currentData.label = data.label
      dictionary.value.currentData.code = data.code
    }
    else {
      // 先更新原有节点信息
      const parent = dictionary.value.currentNode.parent
      const children: Dict[] = parent.data.children || parent.data
      const index = children.findIndex(item => item.id === data.id)
      children[index].label = data.label
      children[index].code = data.code
      // 然后找到需要移动到的父节点位置，并将原有节点移动过去
      if (parentId) {
        const findDictionary: any = (list: Dict[], parentId: number) => {
          for (const i in list) {
            if (list[i].id === parentId) {
              return list[i]
            }
            else if (list[i].children) {
              const temp = findDictionary(list[i].children, parentId)
              if (temp) {
                return temp
              }
            }
          }
        }
        const targetNode = findDictionary(dictionary.value.tree, parentId)
        if (!targetNode.children) {
          targetNode.children = []
        }
        targetNode.children.push(children[index])
      }
      else {
        dictionary.value.tree.push(children[index])
      }
      // 最后删除原节点
      children.splice(index, 1)
    }
  }
}
const dictionaryClick = (data: Dict) => {
  pagination.value.page = 1
  dictionaryItem.value.search.dictionaryId = data.id
}

watch(() => dictionaryItem.value.search.dictionaryId, () => {
  getDictionaryItemList()
})

function getDictionaryItemList() {
  dictionaryItem.value.loading = true
  const params = getParams()
  dictionaryItem.value.search.dictionaryId && (params.dictionaryId = dictionaryItem.value.search.dictionaryId)
  dictionaryItem.value.search.title && (params.title = dictionaryItem.value.search.title)
  api.get('pages_example/dictionary/item/list', {
    baseURL: '/mock/',
    params,
  }).then((res: any) => {
    dictionaryItem.value.loading = false
    dictionaryItem.value.dataList = res.data.list
    dictionaryItem.value.dataList.forEach((item: any) => {
      item.enableLoading = false
    })
    pagination.value.total = res.data.total
  })
}

function onChangeEnable(row: any) {
  return new Promise<boolean>((resolve) => {
    ElMessageBox.confirm(`确认${!row.enable ? '启用' : '禁用'}「${row.name}」吗？`, '确认信息').then(() => {
      row.enableLoading = true
      api.post('pages_example/dictionary/item/change/enable', {
        id: row.id,
        enable: !row.enable,
      }, {
        baseURL: '/mock/',
      }).then(() => {
        row.enableLoading = false
        ElMessage.success({
          message: `模拟${!row.enable ? '启用' : '禁用'}成功`,
          center: true,
        })
        return resolve(true)
      }).catch(() => {
        row.enableLoading = false
        return resolve(false)
      })
    }).catch(() => {
      return resolve(false)
    })
  })
}

// 每页数量切换
function sizeChange(size: number) {
  onSizeChange(size).then(() => getDictionaryItemList())
}

// 当前页码切换（翻页）
function currentChange(page = 1) {
  onCurrentChange(page).then(() => getDictionaryItemList())
}

// 字段排序
function sortChange({ prop, order }: { prop: string; order: string }) {
  onSortChange(prop, order).then(() => getDictionaryItemList())
}

function onCreate() {
  dictionaryItem.value.dialog.id = ''
  dictionaryItem.value.dialog.visible = true
}

function onEdit(row: any) {
  dictionaryItem.value.dialog.id = row.id
  dictionaryItem.value.dialog.visible = true
}

function onDelete(row: any) {
  ElMessageBox.confirm(`确认删除「${row.name}」吗？`, '确认信息').then(() => {
    api.post('pages_example/dictionary/item/delete', {
      id: row.id,
    }, {
      baseURL: '/mock/',
    }).then(() => {
      getDictionaryItemList()
      ElMessage.success({
        message: '模拟删除成功',
        center: true,
      })
    })
  }).catch(() => {})
}

function onDeleteMulti(rows: any[]) {
  ElMessageBox.confirm(`确认删除选中的 ${rows.length} 条数据吗？`, '确认信息').then(() => {
    api.post('pages_example/dictionary/item/delete/multi', {
      ids: rows.map(item => item.id),
    }, {
      baseURL: '/mock/',
    }).then(() => {
      getDictionaryItemList()
      ElMessage.success({
        message: '模拟删除成功',
        center: true,
      })
    })
  }).catch(() => {})
}
</script>

<template>
  <div class="absolute-container">
    <page-header title="字典管理" content="页面数据为 Mock 示例数据，非真实数据。" />
    <div class="page-main">
      <LayoutContainer hide-left-side-toggle>
        <template #leftSide>
          <el-button-group class="btns">
            <el-button type="primary" class="add" @click="dictionaryAdd()">
              新增字典
            </el-button>
            <el-button @click="getDictionaryList">
              <template #icon>
                <el-icon>
                  <svg-icon name="i-ep:refresh" />
                </el-icon>
              </template>
            </el-button>
          </el-button-group>
          <el-input v-model="dictionary.search" placeholder="请输入关键词筛选字典" clearable class="search" />
          <el-scrollbar class="tree">
            <ElTree ref="dictionaryRef" :data="dictionary.tree" :filter-node-method="dictionaryFilter as any" default-expand-all @node-click="dictionaryClick">
              <template #default="{ node, data }">
                <div class="custom-tree-node">
                  <div class="label" :title="node.label">
                    {{ node.label }}
                  </div>
                  <div class="code">
                    {{ data.code }}
                  </div>
                  <div class="actions">
                    <el-button-group>
                      <el-button type="primary" plain size="default" @click.stop="dictionaryAdd(data)">
                        <template #icon>
                          <el-icon>
                            <svg-icon name="i-ep:plus" />
                          </el-icon>
                        </template>
                      </el-button>
                      <el-button type="info" plain size="default" @click.stop="dictionaryEdit(node, data)">
                        <template #icon>
                          <el-icon>
                            <svg-icon name="i-ep:edit" />
                          </el-icon>
                        </template>
                      </el-button>
                      <el-button type="danger" plain size="default" @click.stop="dictionaryDelete(node, data)">
                        <template #icon>
                          <el-icon>
                            <svg-icon name="i-ep:delete" />
                          </el-icon>
                        </template>
                      </el-button>
                    </el-button-group>
                  </div>
                </div>
              </template>
            </ElTree>
          </el-scrollbar>
        </template>
        <div v-show="dictionaryItem.search.dictionaryId" class="container">
          <el-space wrap>
            <el-button type="primary" @click="onCreate">
              <template #icon>
                <el-icon>
                  <svg-icon name="i-ep:plus" />
                </el-icon>
              </template>
            </el-button>
            <el-button type="danger" :disabled="!dictionaryItem.selectionDataList.length" @click="onDeleteMulti(dictionaryItem.selectionDataList)">
              <template #icon>
                <el-icon>
                  <svg-icon name="i-ep:delete" />
                </el-icon>
              </template>
            </el-button>
            <el-input v-model="dictionaryItem.search.title" placeholder="请输入关键词筛选字典项" clearable style="width: 200px;" />
            <el-button @click="getDictionaryItemList">
              <template #icon>
                <el-icon>
                  <svg-icon name="i-ep:search" />
                </el-icon>
              </template>
            </el-button>
          </el-space>
          <el-table ref="dictionaryItemRef" v-loading="dictionaryItem.loading" :data="dictionaryItem.dataList" border stripe highlight-current-row height="100%" @sort-change="sortChange" @selection-change="dictionaryItem.selectionDataList = $event">
            <el-table-column type="selection" align="center" fixed />
            <el-table-column prop="name" label="名称" />
            <el-table-column label="键值" align="center" width="150">
              <template #default="scope">
                <el-tag type="info">
                  {{ scope.row.value }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column label="状态" width="100" align="center">
              <template #default="scope">
                <el-switch v-model="scope.row.enable" :loading="scope.row.enableLoading" inline-prompt active-text="启用" inactive-text="禁用" :before-change="() => onChangeEnable(scope.row)" />
              </template>
            </el-table-column>
            <el-table-column label="操作" width="200" align="center">
              <template #default="scope">
                <el-button type="primary" size="small" plain @click="onEdit(scope.row)">
                  编辑
                </el-button>
                <el-button type="danger" size="small" plain @click="onDelete(scope.row)">
                  删除
                </el-button>
              </template>
            </el-table-column>
          </el-table>
          <el-pagination :current-page="pagination.page" :total="pagination.total" :page-size="pagination.size" :page-sizes="pagination.sizes" :layout="pagination.layout" :hide-on-single-page="false" class="pagination" background @size-change="sizeChange" @current-change="currentChange" />
        </div>
        <div v-show="!dictionaryItem.search.dictionaryId" class="container">
          <div class="empty">
            请在左侧新增或选择一个字典
          </div>
        </div>
      </LayoutContainer>
      <dictionaryDialog v-if="dictionary.dialog.visible" :id="dictionary.dialog.id" v-model="dictionary.dialog.visible" :parent-id="dictionary.dialog.parentId" :tree="dictionary.tree" @add-node="dictionaryAddNode" @edit-node="dictionaryEditNode" />
      <dictionaryItemDialog v-if="dictionaryItem.dialog.visible" :id="dictionaryItem.dialog.id" v-model="dictionaryItem.dialog.visible" :dictionary-id="dictionaryItem.search.dictionaryId" :tree="dictionary.tree" @success="getDictionaryItemList" />
    </div>
  </div>
</template>

<style lang="scss" scoped>
.absolute-container {
  position: absolute;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;

  .page-header {
    margin-bottom: 0;
  }

  .page-main {
    // 让 page-main 的高度自适应
    flex: 1;
    overflow: auto;
    display: flex;
    flex-direction: column;

    .flex-container {
      position: static;
    }
  }
}

.flex-container {
  :deep(.left-side) {
    display: flex;
    flex-direction: column;
    height: 100%;

    .btns {
      display: inline-flex;
      width: 100%;

      .add {
        width: 100%;
      }
    }

    .search {
      margin: 15px 0;
    }

    .tree {
      flex: 1;
      overflow-y: auto;

      .el-tree {
        .el-tree-node__content {
          height: 60px;
        }

        .is-current > .el-tree-node__content {
          background-color: var(--el-color-primary-light-9);
        }

        .custom-tree-node {
          flex: 1;
          position: relative;
          width: 0;
          height: 100%;
          display: flex;
          flex-direction: column;
          justify-content: center;

          .label {
            width: calc(100% - 10px);
            color: var(--el-text-color-primary);

            @include text-overflow;
          }

          .code {
            width: calc(100% - 10px);
            color: var(--el-text-color-placeholder);

            @include text-overflow;
          }

          &:hover {
            .actions {
              display: block;
            }
          }

          .actions {
            display: none;
            position: absolute;
            right: 10px;
            top: 50%;
            transform: translateY(-50%);

            .el-button {
              padding: 5px 8px;
            }
          }
        }
      }
    }
  }

  :deep(.main) {
    display: flex;
    justify-content: center;
  }

  .container {
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 100%;
    height: 100%;

    .empty {
      text-align: center;
      font-size: 32px;
      color: var(--el-text-color-placeholder);
    }

    .el-table {
      margin: 15px 0;
    }
  }
}
</style>
