<template>
  <div>
    <PlusPage
        ref="plusPageInstance"
        :columns="tableConfig"
        :request="getList"
        :before-search-submit="handleBeforeSearch"
        :is-card="true"
        :search="{
          labelWidth: 100,
          showNumber: 3
        }"
        :table="{
          actionBar: { buttons, type: 'link', width: 140 },
          adaptive: { offsetBottom: 50 },
          treeProps: { children: 'children' },
          rowKey: 'menuId'
        }"
        :pagination="false"
    >
      <template #table-title>
        <el-row class="button-row">
          <el-button size="default" type="success" @click="handleCreate">新增</el-button>
        </el-row>
      </template>
    </PlusPage>
    <!-- 弹窗编辑 -->
    <PlusDialogForm
        ref="dialogForm"
        v-model="form"
        v-model:visible="dialogVisible"
        @confirm="handleSubmit"
        @close="handleClose"
        :form="{ columns, labelPosition: 'right',labelWidth: 100, rules, rowProps: {gutter: 20}, colProps: {span: 12} }"
        :dialog="{ title: dialogTitle + '菜单', width: 800, confirmLoading }"
    />
  </div>
</template>

<script lang="ts" setup>
import {computed, defineOptions, h, nextTick, onMounted, reactive, ref, toRefs} from "vue";
import type {FormRules} from 'element-plus'
import {ElMessage} from "element-plus";
import {
  type FieldValues,
  type PlusColumn,
  PlusDialogForm,
  PlusPage,
  PlusPageInstance,
  useTable
} from "plus-pro-components";
import {cloneDeep} from "lodash-es";
import {
  addMenu,
  deleteMenu,
  getSystemList,
  getSystemMenuDetailById,
  getSystemMenuList,
  updateMenu
} from "@/api/system/menu";

defineOptions({
  name: "PageTable"
});

interface TableRow {
  id: number;
  name: string;
  status: string;
  tag: string;
  time: Date;
}

onMounted(async () => {
  await getSystem()
})


// 菜单列表
const menuList = ref([]);
const plusPageInstance = ref<PlusPageInstance | null>(null)

const getList = async (query: Record<string, any>) => {
  let res = await getSystemMenuList(query);
  let list = listToTree(res.data);
  menuList.value = list;
  return {
    data: list,
    total: list.length,
  }
};

const listToTree = (data: object[]) => {
  // * 先生成parent建立父子关系
  const obj = {};
  data.forEach((item: any) => {
    obj[item.menuId] = item;
  });
  // * obj -> {1001: {id: 1001, parentId: 0, name: 'AA'}, 1002: {...}}
  // console.log(obj, "obj")
  const parentList = [];
  data.forEach((item: any) => {
    const parent = obj[item.parentId];
    if (parent) {
      // * 当前项有父节点
      parent.children = parent.children || [];
      parent.children.push(item);
    } else {
      // * 当前项没有父节点 -> 顶层
      parentList.push(item);
    }
  });
  return parentList;
}

// 重新请求列表接口
const refresh = () => {
  plusPageInstance.value?.getList()
}

// 搜索之前函数
const handleBeforeSearch = (values: any) => {
  // 返回新的参数
  return cloneDeep(values);
};

const dialogTitle = computed(() => (state.isCreate ? '新增' : '编辑'))

const {buttons} = useTable<TableRow[]>();

const systemList = ref([]);

const getSystem = async () => {
  // 获取系统列表
  let res = await getSystemList();
  systemList.value = res.data.map((item: any) => ({
    label: item.sysName,
    value: item.sysCode
  }))
}

// 表格数据
const tableConfig: PlusColumn[] = [
  {
    label: "菜单名称",
    prop: "menuName",
    width: 180,
    tableColumnProps: {
      showOverflowTooltip: true
    },
  },
  {
    label: "图标",
    prop: "icon",
    hideInSearch: true,
    width: 80,
    // 返回一个组件
    render: (value) =>
        h('i',
            {
              class: `${value} iconfont`,
              style: {fontSize: '16px'}
            },
        )
  },
  {
    label: "排序",
    prop: "orderNum",
    width: 80,
    hideInSearch: true
  },
  {
    label: "权限标识",
    prop: "perms",
    hideInSearch: true
  },
  {
    label: "网关URL",
    prop: "url",
    hideInSearch: true
  },
  {
    label: "组件路径",
    prop: "component",
    hideInSearch: true
  },
  {
    label: "状态",
    prop: "status",
    valueType: "tag",
    hideInSearch: true,
    fieldProps: value => ({
      type: value === '0' ? 'primary' : 'danger'
    }),
    fieldSlots: {
      default: ({value}) => (value === '0' ? '正常' : '停用')
    }
  },
  {
    label: "系统",
    prop: "systemCode",
    valueType: "select",
    options: computed(() => systemList.value),
    hideInTable: true,
    fieldProps: {
      clearable: true,
      placeholder: '请选择系统',
      style: {width: '100%'}
    }
  },
  {
    label: "菜单状态",
    prop: "status",
    valueType: "select",
    options: [{label: '正常', value: '0'}, {label: '停用', value: '1'}],
    hideInTable: true,
    fieldProps: {
      clearable: true,
      placeholder: '请选择菜单状态',
      style: {width: '100%'}
    }
  },
  {
    label: "创建时间",
    prop: "createTime",
    valueType: "date-picker",
    hideInSearch: true,
    minWidth: 180
  }
];

/*--------------------表单--------------------*/

// 表单实例
const dialogForm = ref(null);

interface State {
  dialogVisible: boolean;
  detailsVisible: boolean;
  confirmLoading: boolean;
  selectedIds: number[];
  isCreate: boolean;
  form: {
    component: string, // 组件路径
    icon: string, // 菜单图标
    isCache: string, // 是否缓存 （0缓存 1不缓存）
    isFrame: string, // 是否外链 （0是 1否）
    menuName: string,// 菜单名称
    menuType: string, // 菜单类型（M目录 C菜单 F按钮）
    orderNum: string,// 显示排序
    parentId: number,// 上级菜单
    path: string, // 路由地址
    perms: string, // 权限标识
    query: string, // 路由参数
    routeName: string, // 路由名称
    status: string, // 菜单状态（0正常 1停用）
    systemCode: string, // 系统编码
    url: string, // 网关URL
    urlmatch: string, // 网关权限
    visible: string, // 显示状态（0显示 1隐藏）
  };
  rules: FormRules;
}

const state = reactive<State>({
  dialogVisible: false,
  detailsVisible: false,
  confirmLoading: false,
  selectedIds: [],
  isCreate: false,
  form: {
    component: null, // 组件路径
    icon: null, // 菜单图标
    isCache: '0', // 是否缓存 （0缓存 1不缓存）
    isFrame: '1', // 是否外链 （0是 1否）
    menuName: null,// 菜单名称
    menuType: 'M', // 菜单类型（M目录 C菜单 F按钮）
    orderNum: null,// 显示排序
    parentId: 0,// 上级菜单
    path: null, // 路由地址
    perms: null, // 权限标识
    query: null, // 路由参数
    routeName: null, // 路由名称
    status: '0', // 菜单状态（0正常 1停用）
    systemCode: null, // 系统编码
    url: null, // 网关URL
    urlmatch: null, // 网关权限
    visible: '0', // 显示状态（0显示 1隐藏）
  },
  rules: {
    parentId: [{required: true, message: '请选择上级菜单', trigger: 'blur'}],
    orderNum: [{required: true, message: '请输入排序', trigger: 'blur'}],
    menuName: [{required: true, message: '请输入菜单名称', trigger: 'blur'}],
    path: [{required: true, message: '请输入路由地址', trigger: 'blur'}],
  }
})

const columns: PlusColumn[] = [
  {
    label: '上级菜单',
    prop: 'parentId',
    colProps: {span: 24},
    valueType: 'tree-select',
    fieldProps: {
      placeholder: '请选择上级菜单',
      clearable: true,
      showSearch: true,
      checkStrictly: true,
      defaultExpandAll: false,
      style: {width: '100%'},
      data: computed(() => {
        return [{menuId: 0, menuName: '主类目', children: [...menuList.value]},]
      }),
      // el-tree-select 组件属性
      props: {
        label: 'menuName',
        value: 'menuId',
        children: 'children'
      }
    }
  },
  {
    label: "系统编码",
    prop: "systemCode",
    valueType: 'input',
    colProps: {span: 24},
    fieldProps: {
      placeholder: '请输入系统编码',
      clearable: true
    }
  },
  {
    label: "菜单类型",
    prop: "menuType",
    valueType: 'radio',
    colProps: {span: 24},
    fieldProps: {
      placeholder: '请选择菜单类型',
      clearable: true,
      options: [
        {label: '目录', value: 'M'},
        {label: '菜单', value: 'C'},
        {label: '按钮', value: 'F'}
      ],
    },
  },
  {
    label: "菜单图标",
    prop: "icon",
    valueType: 'input',
    colProps: {span: 12},
    fieldProps: {
      placeholder: '请输入菜单图标',
      clearable: true,
    },
    hideInForm: computed(() => form.value.menuType === 'F')
  },
  {
    label: "显示排序",
    prop: "orderNum",
    valueType: 'input-number',
    colProps: {span: 12},
    fieldProps: {
      placeholder: '请输入显示排序',
      style: {width: '100%'},
      min: 0
    }
  },
  {
    label: "菜单名称",
    prop: "menuName",
    colProps: {span: 12},
    fieldProps: {
      placeholder: '请输入菜单名称',
      clearable: true
    }
  },
  {
    label: "路由名称",
    prop: "routeName",
    colProps: {span: 12},
    fieldProps: {
      placeholder: '请输入路由名称',
      clearable: true
    },
    hideInForm: computed(() => form.value.menuType !== 'C')
  },
  {
    label: "是否外链",
    prop: "isFrame",
    valueType: 'radio',
    colProps: {span: 12},
    fieldProps: {
      options: [
        {label: '是', value: '0'},
        {label: '否', value: '1'}
      ]
    },
    tooltip: '选择是外链则路由地址需要以`http(s)://`开头',
    hideInForm: computed(() => form.value.menuType === 'F')
  },
  {
    label: "路由地址",
    prop: "path",
    colProps: {span: 12},
    fieldProps: {
      placeholder: '请输入路由地址',
      clearable: true
    },
    tooltip: "访问的路由地址，如：`role`，如外网地址需内链访问则以`http(s)://`开头",
    hideInForm: computed(() => form.value.menuType === 'F')
  },
  {
    label: "组件路径",
    prop: "component",
    colProps: {span: 12},
    fieldProps: {
      placeholder: '请输入组件路径',
      clearable: true
    },
    tooltip: "访问的组件路径，如：`system/role/index`，默认在`views`目录下",
    hideInForm: computed(() => form.value.menuType !== 'C')
  },
  {
    label: "权限字符",
    prop: "perms",
    colProps: {span: 12},
    fieldProps: {
      placeholder: '请输入权限标识',
      clearable: true
    },
    tooltip: "控制器中定义的权限字符，如：@PreAuthorize(`@ss.hasPermi('system:role:list')`)",
    hideInForm: computed(() => form.value.menuType === 'M')
  },
  {
    label: "网关URL",
    prop: "url",
    colProps: {span: 12},
    fieldProps: {
      placeholder: '请输入URL',
      clearable: true
    },
  },
  {
    label: "网关权限",
    prop: "urlmatch",
    colProps: {span: 12},
    fieldProps: {
      placeholder: '请输入网关权限标识',
      clearable: true
    },
    tooltip: "控制器中定义的权限字符",
  },
  {
    label: "路由参数",
    prop: "query",
    colProps: {span: 12},
    fieldProps: {
      placeholder: '请输入路由参数',
      clearable: true
    },
    tooltip: "访问路由的默认传递参数，如：`{\"id\": 1, \"name\": \"ry\"}`",
    hideInForm: computed(() => form.value.menuType !== 'C')
  },
  {
    label: "是否缓存",
    prop: "isCache",
    valueType: 'radio',
    colProps: {span: 12},
    fieldProps: {
      options: [
        {label: '是', value: '0'},
        {label: '否', value: '1'}
      ]
    },
    tooltip: "选择是则会被`keep-alive`缓存，需要匹配组件的`name`和地址保持一致",
    hideInForm: computed(() => form.value.menuType !== 'C')
  },
  {
    label: "显示状态",
    prop: "visible",
    valueType: 'radio',
    colProps: {span: 12},
    fieldProps: {
      options: [
        {label: '显示', value: '0'},
        {label: '隐藏', value: '1'}
      ]
    },
    tooltip: "选择隐藏则路由将不会出现在侧边栏，但仍然可以访问",
    hideInForm: computed(() => form.value.menuType === 'F')
  },
  {
    label: "菜单状态",
    prop: "status",
    valueType: 'radio',
    colProps: {span: 12},
    fieldProps: {
      options: [
        {label: '正常', value: '0'},
        {label: '停用', value: '1'}
      ]
    },
    tooltip: "选择停用则路由将不会出现在侧边栏，也不能被访问"
  },
]

// 创建
const handleCreate = (): void => {
  form.value = {
    component: null, // 组件路径
    icon: null, // 菜单图标
    isCache: '0', // 是否缓存 （0缓存 1不缓存）
    isFrame: '1', // 是否外链 （0是 1否）
    menuName: null,// 菜单名称
    menuType: 'M', // 菜单类型（M目录 C菜单 F按钮）
    orderNum: null,// 显示排序
    parentId: 0,// 上级菜单
    path: null, // 路由地址
    perms: null, // 权限标识
    query: null, // 路由参数
    routeName: null, // 路由名称
    status: '0', // 菜单状态（0正常 1停用）
    systemCode: null, // 系统编码
    url: null, // 网关URL
    urlmatch: null, // 网关权限
    visible: '0', // 显示状态（0显示 1隐藏）
  }
  state.isCreate = true
  state.dialogVisible = true
}

const handleSubmit = async (values: FieldValues) => {
  console.log(values, 'Submit')
  confirmLoading.value = true
  if (state.isCreate) {
    try {
      let params = form.value
      let res = await addMenu(params)
      if (res.code === 200) {
        ElMessage.success('新增成功')
        confirmLoading.value = false
        dialogVisible.value = false
        refresh()
      } else {
        ElMessage.error(res.msg)
      }
    } finally {
      confirmLoading.value = false
    }
  } else {
    // 编辑
    try {
      let params = form.value
      let res = await updateMenu(params)
      if (res.code === 200) {
        ElMessage.success('修改成功')
        confirmLoading.value = false
        dialogVisible.value = false
        refresh()
      } else {
        ElMessage.error(res.msg)
      }
    } finally {
      confirmLoading.value = false
    }
  }
}

const handleClose = () => {
  // 重置表单
  nextTick(() => {
    if (dialogForm.value) {
      dialogForm.value.formInstance.resetFields()
    }
  })
  console.log(dialogForm.value.formInstance)
}

buttons.value = [
  {
    // 修改
    text: "修改",
    code: "edit",
    // props v0.1.16 版本新增函数类型
    props: {
      type: "primary"
    },
    onClick(params) {
      getSystemMenuDetailById(params.row.menuId).then(res => {
        form.value = res.data
        state.isCreate = false
        state.dialogVisible = true
      })
    },
  },
  {
    // 新增
    text: "新增",
    code: "view",
    props: {
      type: "success"
    },
    onClick(params) {
      console.log("新增", params)
      form.value = {
        component: null, // 组件路径
        icon: null, // 菜单图标
        isCache: '0', // 是否缓存 （0缓存 1不缓存）
        isFrame: '1', // 是否外链 （0是 1否）
        menuName: null,// 菜单名称
        menuType: 'M', // 菜单类型（M目录 C菜单 F按钮）
        orderNum: null,// 显示排序
        parentId: params.row.menuId,// 上级菜单
        path: null, // 路由地址
        perms: null, // 权限标识
        query: null, // 路由参数
        routeName: null, // 路由名称
        status: '0', // 菜单状态（0正常 1停用）
        systemCode: null, // 系统编码
        url: null, // 网关URL
        urlmatch: null, // 网关权限
        visible: '0', // 显示状态（0显示 1隐藏）
      }
      state.isCreate = true
      state.dialogVisible = true
    },
  },
  {
    // 删除
    text: "删除",
    code: "delete",
    // props v0.1.16 版本新增计算属性支持
    props: computed(() => ({type: "danger"})),
    confirm: {
      options: {
        draggable: true,
        message: "确定删除此数据吗？"
      }
    },
    onConfirm: async (params) => {
      try {
        let res = await deleteMenu(params.row.menuId)
        if (res.code === 200) {
          ElMessage.success('删除成功')
          refresh()
        } else {
          ElMessage.error(res.msg)
        }
      } catch (e) {
        ElMessage.error('删除失败')
      }
    }
  }
];

const {form, confirmLoading, rules, dialogVisible} = toRefs(state)
</script>
