﻿<template>
  <div class="page-shell">
    <section class="page-card table-fill">
      <div class="page-toolbar">
        <span class="text-sm font-semibold text-slate-700">组织架构</span>
        <n-button type="primary" @click="openCreate()">新增组织</n-button>
      </div>
      <div class="card-body card-body-fill tree-card-body">
        <n-tree
          block-line
          expand-on-click
          :data="treeData"
          key-field="id"
          label-field="name"
          children-field="children"
          :render-switcher-icon="renderSwitcherIcon"
          :render-label="renderLabel"
          :expanded-keys="expandedKeys"
          @update:expanded-keys="onExpanded"
        />
      </div>
    </section>

    <n-modal
      v-model:show="modal.visible"
      preset="card"
      :title="modal.mode === 'create' ? '新增组织' : '编辑组织'"
      class="w-[520px]"
    >
      <n-form ref="formRef" :model="form" :rules="rules" label-placement="left" label-width="90">
        <n-form-item label="上级组织">
          <n-tree-select
            v-model:value="form.parentId"
            clearable
            :options="treeSelectOptions"
            placeholder="可选"
            key-field="id"
            label-field="name"
            children-field="children"
          />
        </n-form-item>
        <n-form-item label="组织名称" path="name"
          ><n-input v-model:value="form.name"
        /></n-form-item>
        <n-form-item label="组织编码" path="code"
          ><n-input v-model:value="form.code" :disabled="modal.mode === 'edit'"
        /></n-form-item>
        <n-form-item label="排序"
          ><n-input-number v-model:value="form.sort" :min="0"
        /></n-form-item>
        <n-form-item label="状态">
          <n-radio-group v-model:value="form.status">
            <n-radio-button value="ENABLED">启用</n-radio-button>
            <n-radio-button value="DISABLED">禁用</n-radio-button>
          </n-radio-group>
        </n-form-item>
      </n-form>
      <template #footer>
        <div class="flex justify-end gap-2">
          <n-button @click="modal.visible = false">取消</n-button>
          <n-button type="primary" :loading="saveMutation.isPending.value" @click="save"
            >保存</n-button
          >
        </div>
      </template>
    </n-modal>
  </div>
</template>

<script setup lang="ts">
import { computed, h, reactive, ref } from 'vue'
import type { FormInst, FormRules, TreeOption } from 'naive-ui'
import { NButton, NPopconfirm, NSpace, NTag, useMessage } from 'naive-ui'
import { ChevronRight } from 'lucide-vue-next'
import { useMutation, useQuery } from '@tanstack/vue-query'
import { createOrgApi, deleteOrgApi, listOrgsApi, updateOrgApi } from '@/api/system/org'
import type { OrgTreeNode } from '@/types/system/org'
import { statusLabel, statusTagType } from '@/utils/display'

const message = useMessage()
const formRef = ref<FormInst | null>(null)
const expandedKeys = ref<string[]>([])

const modal = reactive({ visible: false, mode: 'create' as 'create' | 'edit', id: '' })
const form = reactive({
  parentId: null as string | null,
  name: '',
  code: '',
  sort: 0,
  status: 'ENABLED'
})
const rules: FormRules = {
  name: [{ required: true, message: '请输入组织名称', trigger: ['blur', 'input'] }],
  code: [{ required: true, message: '请输入组织编码', trigger: ['blur', 'input'] }]
}

const orgQuery = useQuery({
  queryKey: ['system', 'orgs'],
  queryFn: listOrgsApi
})

const treeData = computed(() => orgQuery.data.value ?? [])
const treeSelectOptions = computed(() => treeData.value as unknown as TreeOption[])

const saveMutation = useMutation({
  mutationFn: async () => {
    const payload = {
      parentId: form.parentId ?? undefined,
      name: form.name.trim(),
      code: form.code.trim(),
      sort: form.sort,
      status: form.status
    }
    if (modal.mode === 'create') return createOrgApi(payload)
    return updateOrgApi(modal.id, payload)
  },
  onSuccess: async () => {
    message.success('保存成功')
    modal.visible = false
    await orgQuery.refetch()
    expandedKeys.value = collectAllKeys(treeData.value)
  }
})

const deleteMutation = useMutation({
  mutationFn: deleteOrgApi,
  onSuccess: async () => {
    message.success('删除成功')
    await orgQuery.refetch()
  }
})

function openCreate(parentId?: string) {
  modal.mode = 'create'
  modal.id = ''
  form.parentId = parentId ?? null
  form.name = ''
  form.code = ''
  form.sort = 0
  form.status = 'ENABLED'
  modal.visible = true
}

function openEdit(node: OrgTreeNode) {
  modal.mode = 'edit'
  modal.id = node.id
  form.parentId = node.parentId ?? null
  form.name = node.name
  form.code = node.code
  form.sort = node.sort
  form.status = node.status
  modal.visible = true
}

async function save() {
  await formRef.value?.validate()
  await saveMutation.mutateAsync()
}

function renderLabel(payload: { option: unknown }) {
  const node = payload.option as OrgTreeNode
  return h('div', { class: 'flex w-full items-center justify-between py-2' }, [
    h('div', { class: 'flex items-center gap-2' }, [
      h('span', node.name),
      h(
        NTag,
        { size: 'small', type: statusTagType(node.status) },
        { default: () => statusLabel(node.status) }
      )
    ]),
    h(NSpace, { size: 6 }, () => [
      h(
        NButton,
        { size: 'small', tertiary: true, onClick: () => openCreate(node.id) },
        { default: () => '新增子级' }
      ),
      h(
        NButton,
        { size: 'small', tertiary: true, type: 'primary', onClick: () => openEdit(node) },
        { default: () => '编辑' }
      ),
      h(
        NPopconfirm,
        { onPositiveClick: () => deleteMutation.mutate(node.id) },
        {
          trigger: () =>
            h(NButton, { size: 'small', tertiary: true, type: 'error' }, { default: () => '删除' }),
          default: () => '确认删除该组织吗？'
        }
      )
    ])
  ])
}
function onExpanded(keys: string[]) {
  expandedKeys.value = keys
}

function collectAllKeys(nodes: OrgTreeNode[]): string[] {
  return nodes.flatMap((node) => [node.id, ...collectAllKeys(node.children ?? [])])
}

function renderSwitcherIcon() {
  return h(ChevronRight, { size: 16, strokeWidth: 2.25, class: 'tree-switcher-icon' })
}
</script>

<style scoped>
.tree-card-body {
  padding: 8px 10px 12px;
}

:deep(.n-tree-node-switcher) {
  width: 28px;
  height: 42px;
  margin-top: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  align-self: stretch;
}

:deep(.tree-switcher-icon) {
  display: block;
  color: #0f8f86;
}

:deep(.n-tree-node-content) {
  min-height: 42px;
  align-items: center;
  border-radius: 14px;
}

:deep(.n-tree-node-content__text) {
  flex: 1;
  min-width: 0;
  padding: 0;
}
</style>
