<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import {
  getRolesByPage,
  getAllRoles,
  createRole,
  updateRole,
  deleteRole,
  getRolePermissions,
  assignPermissionsToRole,
  getAllPermissions,
  createPermission,
  updatePermission,
  deletePermission,
  groupPermissionsByModule,
  type SysRole,
  type SysPermission,
  type RolePageResponse,
  type PermissionGroup,
} from '@/api/role'
import { verifyAdminPassword } from '@/api/member'
import AppLayout from '@/components/AppLayout.vue'
import { useToast } from '@/composables/useToast'

const toast = useToast()

// ==================== Tab 切换 ====================
const activeTab = ref<'role' | 'permission'>('role')

// ==================== 角色管理 ====================
const roles = ref<SysRole[]>([])
const roleLoading = ref(false)
const roleKeyword = ref('')
const rolePage = ref(0)
const roleSize = ref(10)
const roleTotalElements = ref(0)
const roleTotalPages = ref(0)

// 创建/编辑角色弹窗
const showRoleForm = ref(false)
const roleFormLoading = ref(false)
const isRoleEdit = ref(false)
const editRoleId = ref<number | null>(null)
const roleForm = ref({ roleName: '', roleKey: '', roleSort: 0, status: 1 })

// 角色权限分配弹窗
const showRolePermModal = ref(false)
const rolePermLoading = ref(false)
const permRole = ref<SysRole | null>(null)
const allPerms = ref<SysPermission[]>([])
const rolePerms = ref<SysPermission[]>([])
const selectedPermIds = ref<number[]>([])

// 角色删除确认
const showRoleDelete = ref(false)
const deleteRoleTarget = ref<SysRole | null>(null)
const roleDeleteLoading = ref(false)

// ==================== 权限管理 ====================
const permissions = ref<SysPermission[]>([])
const permLoading = ref(false)

const permGroups = computed<PermissionGroup[]>(() => groupPermissionsByModule(permissions.value))
const bizPermGroups = computed(() => permGroups.value.filter((g) => g.category === 'business'))
const sysPermGroups = computed(() => permGroups.value.filter((g) => g.category === 'system'))
const allPermGroups = computed<PermissionGroup[]>(() => groupPermissionsByModule(allPerms.value))
const allBizGroups = computed(() => allPermGroups.value.filter((g) => g.category === 'business'))
const allSysGroups = computed(() => allPermGroups.value.filter((g) => g.category === 'system'))

/** 权限管理 Tab 系统模块折叠 */
const sysPermCollapsed = ref(true)
/** 角色分配弹窗 系统模块折叠 */
const sysRolePermCollapsed = ref(true)

// ==================== 超级管理员密码验证 ====================
const showAdminPwdModal = ref(false)
const adminPwdInput = ref('')
const adminPwdError = ref('')
const adminPwdLoading = ref(false)
let adminPwdResolve: ((ok: boolean) => void) | null = null

function isProtectedRole(role: SysRole): boolean {
  return role.id === 1
}

function requireAdminPassword(): Promise<boolean> {
  return new Promise((resolve) => {
    adminPwdInput.value = ''
    adminPwdError.value = ''
    adminPwdResolve = resolve
    showAdminPwdModal.value = true
  })
}

async function confirmAdminPassword() {
  if (!adminPwdInput.value) {
    adminPwdError.value = '请输入超级管理员密码'
    return
  }
  adminPwdLoading.value = true
  adminPwdError.value = ''
  try {
    const ok = await verifyAdminPassword(adminPwdInput.value)
    if (ok) {
      showAdminPwdModal.value = false
      adminPwdResolve?.(true)
    } else {
      adminPwdError.value = '密码错误'
    }
  } catch {
    adminPwdError.value = '验证失败'
  } finally {
    adminPwdLoading.value = false
  }
}

function cancelAdminPassword() {
  showAdminPwdModal.value = false
  adminPwdResolve?.(false)
}


// 创建/编辑权限弹窗
const showPermForm = ref(false)
const permFormLoading = ref(false)
const isPermEdit = ref(false)
const editPermId = ref<number | null>(null)
const permForm = ref({
  permissionName: '',
  permissionCode: '',
  resource: '',
  action: 'GET',
  description: '',
  status: 1,
})

// 权限删除确认
const showPermDelete = ref(false)
const deletePermTarget = ref<SysPermission | null>(null)
const permDeleteLoading = ref(false)

const actionOptions = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']

const statusMap: Record<number, string> = { 0: '禁用', 1: '正常' }

// ==================== 角色列表 ====================
async function fetchRoles() {
  roleLoading.value = true
  try {
    const res: RolePageResponse = await getRolesByPage({
      page: rolePage.value,
      size: roleSize.value,
      sort: 'roleSort',
      order: 'asc',
      keyword: roleKeyword.value || undefined,
    })
    roles.value = res.content
    roleTotalElements.value = res.totalElements
    roleTotalPages.value = res.totalPages
  } catch (e: any) {
    const msg = e?.response?.data?.error || e?.response?.data?.message || e?.message || '获取角色列表失败'
    toast.error(msg)
  } finally {
    roleLoading.value = false
  }
}

function roleSearch() {
  rolePage.value = 0
  fetchRoles()
}

function roleGoPage(page: number) {
  if (page < 0 || page >= roleTotalPages.value) return
  rolePage.value = page
  fetchRoles()
}

// ==================== 角色 CRUD ====================
function openRoleCreate() {
  isRoleEdit.value = false
  editRoleId.value = null
  roleForm.value = { roleName: '', roleKey: '', roleSort: 0, status: 1 }
  showRoleForm.value = true
}

function openRoleEdit(role: SysRole) {
  isRoleEdit.value = true
  editRoleId.value = role.id
  roleForm.value = {
    roleName: role.roleName,
    roleKey: role.roleKey,
    roleSort: role.roleSort,
    status: role.status,
  }
  showRoleForm.value = true
}

async function handleRoleSave() {
  if (isRoleEdit.value) {
    showRoleForm.value = false
    const ok = await requireAdminPassword()
    if (!ok) { showRoleForm.value = true; return }
  }
  roleFormLoading.value = true
  try {
    if (isRoleEdit.value && editRoleId.value) {
      await updateRole(editRoleId.value, roleForm.value)
      toast.success('角色更新成功')
    } else {
      await createRole(roleForm.value)
      toast.success('角色创建成功')
    }
    fetchRoles()
  } catch (e: any) {
    toast.error(e?.response?.data?.error || e?.response?.data?.message || '操作失败')
  } finally {
    roleFormLoading.value = false
  }
}

function openRoleDelete(role: SysRole) {
  deleteRoleTarget.value = role
  showRoleDelete.value = true
}

async function handleRoleDelete() {
  if (!deleteRoleTarget.value) return
  showRoleDelete.value = false
  const ok = await requireAdminPassword()
  if (!ok) return
  roleDeleteLoading.value = true
  try {
    const target = deleteRoleTarget.value
    await deleteRole(target.id)
    toast.success('角色已删除')
    fetchRoles()
  } catch (e: any) {
    const msg = e?.response?.data?.error || e?.response?.data?.message || e?.message || '删除失败'
    toast.error(msg)
  } finally {
    roleDeleteLoading.value = false
  }
}

// ==================== 角色权限分配 ====================
async function openRolePermModal(role: SysRole) {
  permRole.value = role
  showRolePermModal.value = true
  rolePermLoading.value = true
  try {
    const [all, assigned] = await Promise.all([
      getAllPermissions(),
      getRolePermissions(role.id),
    ])
    allPerms.value = all
    rolePerms.value = assigned
    selectedPermIds.value = assigned.map((p) => p.id)
  } catch (e: any) {
    const msg = e?.response?.data?.error || e?.response?.data?.message || e?.message || '获取权限信息失败'
    toast.error(msg)
  } finally {
    rolePermLoading.value = false
  }
}

function togglePerm(permId: number) {
  const idx = selectedPermIds.value.indexOf(permId)
  if (idx >= 0) {
    selectedPermIds.value.splice(idx, 1)
  } else {
    selectedPermIds.value.push(permId)
  }
}

async function handleAssignPerms() {
  if (!permRole.value) return
  showRolePermModal.value = false
  const ok = await requireAdminPassword()
  if (!ok) { showRolePermModal.value = true; return }
  rolePermLoading.value = true
  try {
    await assignPermissionsToRole(permRole.value.id, selectedPermIds.value)
    toast.success('权限分配成功')
  } catch (e: any) {
    const msg = e?.response?.data?.error || e?.response?.data?.message || e?.message || '权限分配失败'
    toast.error(msg)
  } finally {
    rolePermLoading.value = false
  }
}

// ==================== 权限列表 ====================
async function fetchPermissions() {
  permLoading.value = true
  try {
    permissions.value = await getAllPermissions()
  } catch (e: any) {
    const msg = e?.response?.data?.error || e?.response?.data?.message || e?.message || '获取权限列表失败'
    toast.error(msg)
  } finally {
    permLoading.value = false
  }
}

// ==================== 权限 CRUD ====================
function openPermCreate() {
  isPermEdit.value = false
  editPermId.value = null
  permForm.value = { permissionName: '', permissionCode: '', resource: '', action: 'GET', description: '', status: 1 }
  showPermForm.value = true
}

function openPermEdit(perm: SysPermission) {
  isPermEdit.value = true
  editPermId.value = perm.id
  permForm.value = {
    permissionName: perm.permissionName,
    permissionCode: perm.permissionCode,
    resource: perm.resource,
    action: perm.action,
    description: perm.description || '',
    status: perm.status,
  }
  showPermForm.value = true
}

async function handlePermSave() {
  permFormLoading.value = true
  try {
    if (isPermEdit.value && editPermId.value) {
      await updatePermission(editPermId.value, {
        id: editPermId.value,
        ...permForm.value,
        createdAt: '',
        updatedAt: '',
      } as SysPermission)
      toast.success('权限更新成功')
    } else {
      await createPermission(permForm.value)
      toast.success('权限创建成功')
    }
    showPermForm.value = false
    fetchPermissions()
  } catch (e: any) {
    toast.error(e?.response?.data?.error || e?.response?.data?.message || '操作失败')
  } finally {
    permFormLoading.value = false
  }
}

function openPermDelete(perm: SysPermission) {
  deletePermTarget.value = perm
  showPermDelete.value = true
}

async function handlePermDelete() {
  if (!deletePermTarget.value) return
  permDeleteLoading.value = true
  try {
    await deletePermission(deletePermTarget.value.id)
    toast.success('权限已删除')
    showPermDelete.value = false
    fetchPermissions()
  } catch (e: any) {
    const msg = e?.response?.data?.error || e?.response?.data?.message || e?.message || '删除失败'
    toast.error(msg)
  } finally {
    permDeleteLoading.value = false
  }
}

// ==================== 工具函数 ====================
function formatTime(t: string | null): string {
  if (!t) return '--'
  return new Date(t).toLocaleString('zh-CN')
}

// ==================== 初始化 ====================
onMounted(() => {
  fetchRoles()
  fetchPermissions()
})
</script>

<template>
  <AppLayout>
    <!-- ========== Tab 切换 ========== -->
    <div class="tab-bar">
      <button
        class="tab-btn"
        :class="{ active: activeTab === 'role' }"
        @click="activeTab = 'role'"
      >
        <i class="fas fa-user-shield"></i> 角色管理
      </button>
      <button
        class="tab-btn"
        :class="{ active: activeTab === 'permission' }"
        @click="activeTab = 'permission'"
      >
        <i class="fas fa-shield-alt"></i> 权限管理
      </button>
    </div>

    <!-- ============================================ -->
    <!-- ========== 角色管理 Tab ==================== -->
    <!-- ============================================ -->
    <div v-show="activeTab === 'role'">
      <div class="filter-bar">
        <div class="filter-row">
          <input
            v-model="roleKeyword"
            class="filter-input"
            placeholder="搜索角色名称..."
            @keyup.enter="roleSearch"
          />
          <button class="btn btn-primary" @click="roleSearch">
            <i class="fas fa-search"></i> 查询
          </button>
          <button class="btn btn-outline" @click="roleKeyword = ''; roleSearch()">
            <i class="fas fa-redo"></i> 重置
          </button>
          <div class="filter-spacer"></div>
          <button class="btn btn-primary" @click="openRoleCreate">
            <i class="fas fa-plus"></i> 新增角色
          </button>
        </div>
      </div>

      <div class="table-container">
        <table class="data-table" v-if="!roleLoading">
          <thead>
            <tr>
              <th style="width: 70px">ID</th>
              <th style="width: 140px">角色名称</th>
              <th style="width: 140px">角色标识</th>
              <th style="width: 80px">排序</th>
              <th style="width: 70px">状态</th>
              <th style="width: 150px">创建时间</th>
              <th style="width: 240px">操作</th>
            </tr>
          </thead>
          <tbody>
            <tr v-if="roles.length === 0">
              <td colspan="7" class="empty-cell">暂无角色数据</td>
            </tr>
            <tr v-for="role in roles" :key="role.id">
              <td>{{ role.id }}</td>
              <td class="text-bold">{{ role.roleName }}</td>
              <td><code class="code-tag">{{ role.roleKey }}</code></td>
              <td>{{ role.roleSort }}</td>
              <td>
                <span class="status-badge" :class="role.status === 1 ? 'active' : 'inactive'">
                  {{ statusMap[role.status] }}
                </span>
              </td>
              <td>{{ formatTime(role.createdAt) }}</td>
              <td class="action-cell">
                <button class="btn btn-sm btn-outline" :disabled="isProtectedRole(role)" @click="!isProtectedRole(role) && openRolePermModal(role)">
                  <i class="fas fa-lock"></i> 权限
                </button>
                <button class="btn btn-sm btn-outline" :disabled="isProtectedRole(role)" @click="!isProtectedRole(role) && openRoleEdit(role)">
                  <i class="fas fa-edit"></i> 编辑
                </button>
                <button class="btn btn-sm btn-danger" :disabled="isProtectedRole(role)" @click="!isProtectedRole(role) && openRoleDelete(role)">
                  <i class="fas fa-trash"></i>
                </button>
              </td>
            </tr>
          </tbody>
        </table>
        <div v-else class="loading-state">
          <i class="fas fa-spinner fa-spin"></i> 加载中...
        </div>
      </div>

      <!-- 分页 -->
      <div class="pagination-bar" v-if="roleTotalPages > 1">
        <span class="page-info">共 {{ roleTotalElements }} 条，第 {{ rolePage + 1 }}/{{ roleTotalPages }} 页</span>
        <div class="page-btns">
          <button class="btn btn-sm btn-outline" :disabled="rolePage <= 0" @click="roleGoPage(0)">
            <i class="fas fa-angle-double-left"></i>
          </button>
          <button class="btn btn-sm btn-outline" :disabled="rolePage <= 0" @click="roleGoPage(rolePage - 1)">
            <i class="fas fa-angle-left"></i>
          </button>
          <button class="btn btn-sm btn-outline" :disabled="rolePage >= roleTotalPages - 1" @click="roleGoPage(rolePage + 1)">
            <i class="fas fa-angle-right"></i>
          </button>
          <button class="btn btn-sm btn-outline" :disabled="rolePage >= roleTotalPages - 1" @click="roleGoPage(roleTotalPages - 1)">
            <i class="fas fa-angle-double-right"></i>
          </button>
        </div>
      </div>
    </div>

    <!-- ============================================ -->
    <!-- ========== 权限管理 Tab ==================== -->
    <!-- ============================================ -->
    <div v-show="activeTab === 'permission'">
      <div class="filter-bar">
        <div class="filter-row">
          <div class="filter-spacer"></div>
          <button class="btn btn-primary" @click="openPermCreate">
            <i class="fas fa-plus"></i> 新增权限
          </button>
        </div>
      </div>

      <div class="table-container">
        <table class="data-table" v-if="!permLoading">
          <thead>
            <tr>
              <th style="width: 70px">ID</th>
              <th style="width: 140px">权限名称</th>
              <th style="width: 180px">权限编码</th>
              <th style="width: 160px">资源路径</th>
              <th style="width: 70px">操作</th>
              <th style="width: 240px">描述</th>
              <th style="width: 70px">状态</th>
              <th style="width: 130px">操作</th>
            </tr>
          </thead>
          <tbody v-if="permissions.length === 0">
            <tr><td colspan="8" class="empty-cell">暂无权限数据</td></tr>
          </tbody>
          <!-- 业务权限 -->
          <template v-for="group in bizPermGroups" :key="group.moduleLabel">
            <tbody>
              <tr class="group-header-row">
                <td colspan="8">
                  <div class="group-header">
                    <i class="fas fa-store"></i>
                    <span>{{ group.moduleLabel }}</span>
                    <span class="group-badge biz">业务</span>
                    <span class="group-count">{{ group.permissions.length }} 项</span>
                  </div>
                </td>
              </tr>
            </tbody>
            <tbody>
              <tr v-for="perm in group.permissions" :key="perm.id">
                <td>{{ perm.id }}</td>
                <td class="text-bold">{{ perm.permissionName }}</td>
                <td><code class="code-tag">{{ perm.permissionCode }}</code></td>
                <td><code class="code-tag path">{{ perm.resource }}</code></td>
                <td><span class="method-badge" :class="perm.action.toLowerCase()">{{ perm.action }}</span></td>
                <td class="text-muted">{{ perm.description || '--' }}</td>
                <td><span class="status-badge" :class="perm.status === 1 ? 'active' : 'inactive'">{{ statusMap[perm.status] }}</span></td>
                <td class="action-cell">
                  <button class="btn btn-sm btn-outline" @click="openPermEdit(perm)"><i class="fas fa-edit"></i></button>
                  <button class="btn btn-sm btn-danger" @click="openPermDelete(perm)"><i class="fas fa-trash"></i></button>
                </td>
              </tr>
            </tbody>
          </template>
          <!-- 系统权限（折叠） -->
          <tbody v-if="sysPermGroups.length > 0">
            <tr class="group-header-row sys-toggle" @click="sysPermCollapsed = !sysPermCollapsed">
              <td colspan="8">
                <div class="group-header clickable">
                  <i :class="sysPermCollapsed ? 'fas fa-chevron-right' : 'fas fa-chevron-down'"></i>
                  <i class="fas fa-cogs"></i>
                  <span>系统权限（技术维护用）</span>
                  <span class="group-badge sys">系统</span>
                  <span class="group-count">{{ sysPermGroups.reduce((s, g) => s + g.permissions.length, 0) }} 项</span>
                </div>
              </td>
            </tr>
          </tbody>
          <template v-if="!sysPermCollapsed">
            <template v-for="group in sysPermGroups" :key="group.moduleLabel">
              <tbody>
                <tr class="group-header-row sub">
                  <td colspan="8">
                    <div class="group-header">
                      <span>{{ group.moduleLabel }}</span>
                      <span class="group-count">{{ group.permissions.length }} 项</span>
                    </div>
                  </td>
                </tr>
              </tbody>
              <tbody>
                <tr v-for="perm in group.permissions" :key="perm.id">
                  <td>{{ perm.id }}</td>
                  <td class="text-bold">{{ perm.permissionName }}</td>
                  <td><code class="code-tag">{{ perm.permissionCode }}</code></td>
                  <td><code class="code-tag path">{{ perm.resource }}</code></td>
                  <td><span class="method-badge" :class="perm.action.toLowerCase()">{{ perm.action }}</span></td>
                  <td class="text-muted">{{ perm.description || '--' }}</td>
                  <td><span class="status-badge" :class="perm.status === 1 ? 'active' : 'inactive'">{{ statusMap[perm.status] }}</span></td>
                  <td class="action-cell">
                    <button class="btn btn-sm btn-outline" @click="openPermEdit(perm)"><i class="fas fa-edit"></i></button>
                    <button class="btn btn-sm btn-danger" @click="openPermDelete(perm)"><i class="fas fa-trash"></i></button>
                  </td>
                </tr>
              </tbody>
            </template>
          </template>
        </table>
        <div v-else class="loading-state">
          <i class="fas fa-spinner fa-spin"></i> 加载中...
        </div>
      </div>
    </div>

    <!-- ============================================ -->
    <!-- ========== 角色表单弹窗 ==================== -->
    <!-- ============================================ -->
    <Teleport to="body">
      <div class="modal-overlay" v-if="showRoleForm" @click.self="showRoleForm = false">
        <div class="modal-box modal-sm">
          <div class="modal-header">
            <h3>
              <i :class="isRoleEdit ? 'fas fa-edit' : 'fas fa-plus-circle'"></i>
              {{ isRoleEdit ? '编辑角色' : '新增角色' }}
            </h3>
            <button class="modal-close" @click="showRoleForm = false">&times;</button>
          </div>
          <div class="modal-body">
            <div class="form-group">
              <label>角色名称 <span class="required">*</span></label>
              <input v-model="roleForm.roleName" class="form-input" placeholder="例如：普通管理员" />
            </div>
            <div class="form-group">
              <label>角色标识 <span class="required">*</span></label>
              <input v-model="roleForm.roleKey" class="form-input" placeholder="例如：manager" />
            </div>
            <div class="form-row">
              <div class="form-group">
                <label>排序</label>
                <input v-model.number="roleForm.roleSort" type="number" class="form-input" />
              </div>
              <div class="form-group">
                <label>状态</label>
                <select v-model="roleForm.status" class="form-input">
                  <option :value="1">正常</option>
                  <option :value="0">禁用</option>
                </select>
              </div>
            </div>
          </div>
          <div class="modal-footer">
            <button class="btn btn-outline" @click="showRoleForm = false">取消</button>
            <button class="btn btn-primary" :disabled="roleFormLoading" @click="handleRoleSave">
              <i class="fas fa-spinner fa-spin" v-if="roleFormLoading"></i>
              {{ roleFormLoading ? '保存中...' : '保存' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- ========== 管理员密码验证弹窗 ========== -->
    <Teleport to="body">
      <div class="modal-overlay" v-if="showAdminPwdModal" @click.self="cancelAdminPassword">
        <div class="modal-box modal-sm">
          <div class="modal-header">
            <h3><i class="fas fa-lock"></i> 管理员验证</h3>
            <button class="modal-close" @click="cancelAdminPassword">&times;</button>
          </div>
          <div class="modal-body">
            <p class="modal-desc">此操作需要超级管理员密码验证：</p>
            <div class="form-group">
              <label>管理员密码</label>
              <input v-model="adminPwdInput" type="password" class="form-input" placeholder="请输入超级管理员密码" @keyup.enter="confirmAdminPassword" />
              <span class="field-error" v-if="adminPwdError">{{ adminPwdError }}</span>
            </div>
          </div>
          <div class="modal-footer">
            <button class="btn btn-outline" @click="cancelAdminPassword">取消</button>
            <button class="btn btn-primary" :disabled="adminPwdLoading" @click="confirmAdminPassword">
              <i class="fas fa-spinner fa-spin" v-if="adminPwdLoading"></i>
              {{ adminPwdLoading ? '验证中...' : '确认' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- ============================================ -->
    <!-- ========== 角色权限分配弹窗 ================ -->
    <!-- ============================================ -->
    <Teleport to="body">
      <div class="modal-overlay" v-if="showRolePermModal" @click.self="showRolePermModal = false">
        <div class="modal-box">
          <div class="modal-header">
            <h3><i class="fas fa-lock"></i> 分配权限 - {{ permRole?.roleName }}</h3>
            <button class="modal-close" @click="showRolePermModal = false">&times;</button>
          </div>
          <div class="modal-body">
            <p class="modal-desc">为角色 <strong>{{ permRole?.roleName }}</strong> 勾选所需权限：</p>
            <div v-if="allPerms.length === 0" class="empty-cell">暂无可用权限</div>
            <div v-else class="perm-grid">
              <!-- 业务权限 -->
              <template v-for="group in allBizGroups" :key="group.moduleLabel">
                <div class="perm-group-label">
                  <i class="fas fa-store"></i> {{ group.moduleLabel }}
                  <span class="group-badge biz small">业务</span>
                </div>
                <label
                  v-for="perm in group.permissions"
                  :key="perm.id"
                  class="perm-item"
                  :class="{ checked: selectedPermIds.includes(perm.id) }"
                >
                  <div class="perm-check" @click="togglePerm(perm.id)">
                    <i :class="selectedPermIds.includes(perm.id) ? 'fas fa-check-square' : 'far fa-square'"></i>
                    <div class="perm-info">
                      <div class="perm-name">{{ perm.permissionName }}</div>
                      <div class="perm-meta">
                        <code class="code-tag">{{ perm.permissionCode }}</code>
                        <span class="method-badge" :class="perm.action.toLowerCase()">{{ perm.action }}</span>
                        <span class="text-muted" v-if="perm.description">{{ perm.description }}</span>
                      </div>
                    </div>
                  </div>
                </label>
              </template>
              <!-- 系统权限（折叠） -->
              <div
                v-if="allSysGroups.length > 0"
                class="perm-group-label sys-toggle"
                @click="sysRolePermCollapsed = !sysRolePermCollapsed"
              >
                <i :class="sysRolePermCollapsed ? 'fas fa-chevron-right' : 'fas fa-chevron-down'"></i>
                <i class="fas fa-cogs"></i> 系统权限（技术维护用，点击展开）
                <span class="group-badge sys small">系统</span>
              </div>
              <template v-if="!sysRolePermCollapsed">
                <template v-for="group in allSysGroups" :key="group.moduleLabel">
                  <div class="perm-group-label sub">
                    {{ group.moduleLabel }}
                  </div>
                  <label
                    v-for="perm in group.permissions"
                    :key="perm.id"
                    class="perm-item"
                    :class="{ checked: selectedPermIds.includes(perm.id) }"
                  >
                    <div class="perm-check" @click="togglePerm(perm.id)">
                      <i :class="selectedPermIds.includes(perm.id) ? 'fas fa-check-square' : 'far fa-square'"></i>
                      <div class="perm-info">
                        <div class="perm-name">{{ perm.permissionName }}</div>
                        <div class="perm-meta">
                          <code class="code-tag">{{ perm.permissionCode }}</code>
                          <span class="method-badge" :class="perm.action.toLowerCase()">{{ perm.action }}</span>
                          <span class="text-muted" v-if="perm.description">{{ perm.description }}</span>
                        </div>
                      </div>
                    </div>
                  </label>
                </template>
              </template>
            </div>
          </div>
          <div class="modal-footer">
            <button class="btn btn-outline" @click="showRolePermModal = false">取消</button>
            <button class="btn btn-primary" :disabled="rolePermLoading" @click="handleAssignPerms">
              <i class="fas fa-spinner fa-spin" v-if="rolePermLoading"></i>
              {{ rolePermLoading ? '保存中...' : '保存权限' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- ============================================ -->
    <!-- ========== 角色删除确认弹窗 ================ -->
    <!-- ============================================ -->
    <Teleport to="body">
      <div class="modal-overlay" v-if="showRoleDelete" @click.self="showRoleDelete = false">
        <div class="modal-box modal-sm">
          <div class="modal-header">
            <h3><i class="fas fa-exclamation-triangle" style="color: #e74c3c"></i> 确认删除</h3>
            <button class="modal-close" @click="showRoleDelete = false">&times;</button>
          </div>
          <div class="modal-body">
            <p class="modal-desc">
              确定要删除角色 <strong>{{ deleteRoleTarget?.roleName }}</strong> 吗？<br />
              <em style="color: #8b9ab0">（逻辑删除）</em>
            </p>
          </div>
          <div class="modal-footer">
            <button class="btn btn-outline" @click="showRoleDelete = false">取消</button>
            <button class="btn btn-danger" :disabled="roleDeleteLoading" @click="handleRoleDelete">
              <i class="fas fa-spinner fa-spin" v-if="roleDeleteLoading"></i>
              {{ roleDeleteLoading ? '删除中...' : '确认删除' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- ============================================ -->
    <!-- ========== 权限表单弹窗 ==================== -->
    <!-- ============================================ -->
    <Teleport to="body">
      <div class="modal-overlay" v-if="showPermForm" @click.self="showPermForm = false">
        <div class="modal-box modal-sm">
          <div class="modal-header">
            <h3>
              <i :class="isPermEdit ? 'fas fa-edit' : 'fas fa-plus-circle'"></i>
              {{ isPermEdit ? '编辑权限' : '新增权限' }}
            </h3>
            <button class="modal-close" @click="showPermForm = false">&times;</button>
          </div>
          <div class="modal-body">
            <div class="form-group">
              <label>权限名称 <span class="required">*</span></label>
              <input v-model="permForm.permissionName" class="form-input" placeholder="例如：用户查看" />
            </div>
            <div class="form-group">
              <label>权限编码 <span class="required">*</span></label>
              <input v-model="permForm.permissionCode" class="form-input" placeholder="例如：system:user:view" />
            </div>
            <div class="form-group">
              <label>资源路径 <span class="required">*</span></label>
              <input v-model="permForm.resource" class="form-input" placeholder="例如：/api/users" />
            </div>
            <div class="form-row">
              <div class="form-group">
                <label>操作类型 <span class="required">*</span></label>
                <select v-model="permForm.action" class="form-input">
                  <option v-for="a in actionOptions" :key="a" :value="a">{{ a }}</option>
                </select>
              </div>
              <div class="form-group">
                <label>状态</label>
                <select v-model="permForm.status" class="form-input">
                  <option :value="1">正常</option>
                  <option :value="0">禁用</option>
                </select>
              </div>
            </div>
            <div class="form-group">
              <label>描述</label>
              <input v-model="permForm.description" class="form-input" placeholder="权限说明（可选）" />
            </div>
          </div>
          <div class="modal-footer">
            <button class="btn btn-outline" @click="showPermForm = false">取消</button>
            <button class="btn btn-primary" :disabled="permFormLoading" @click="handlePermSave">
              <i class="fas fa-spinner fa-spin" v-if="permFormLoading"></i>
              {{ permFormLoading ? '保存中...' : '保存' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- ============================================ -->
    <!-- ========== 权限删除确认弹窗 ================ -->
    <!-- ============================================ -->
    <Teleport to="body">
      <div class="modal-overlay" v-if="showPermDelete" @click.self="showPermDelete = false">
        <div class="modal-box modal-sm">
          <div class="modal-header">
            <h3><i class="fas fa-exclamation-triangle" style="color: #e74c3c"></i> 确认删除</h3>
            <button class="modal-close" @click="showPermDelete = false">&times;</button>
          </div>
          <div class="modal-body">
            <p class="modal-desc">
              确定要删除权限 <strong>{{ deletePermTarget?.permissionName }}</strong> 吗？<br />
              <code class="code-tag">{{ deletePermTarget?.permissionCode }}</code>
            </p>
          </div>
          <div class="modal-footer">
            <button class="btn btn-outline" @click="showPermDelete = false">取消</button>
            <button class="btn btn-danger" :disabled="permDeleteLoading" @click="handlePermDelete">
              <i class="fas fa-spinner fa-spin" v-if="permDeleteLoading"></i>
              {{ permDeleteLoading ? '删除中...' : '确认删除' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>
  </AppLayout>
</template>

<style scoped>
/* ==================== Tab 切换 ==================== */
.tab-bar {
  display: flex;
  gap: 0;
  margin-bottom: 20px;
  background: #ffffff;
  border-radius: 12px;
  border: 1px solid #e9edf4;
  overflow: hidden;
  width: fit-content;
}

.tab-btn {
  padding: 10px 24px;
  border: none;
  background: transparent;
  font-size: 14px;
  font-weight: 500;
  color: #5a6a85;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  transition: all 0.2s;
}

.tab-btn:hover {
  color: #4f7cff;
  background: #f8f9fc;
}

.tab-btn.active {
  background: #4f7cff;
  color: #ffffff;
}

/* ==================== 筛选栏 ==================== */
.filter-bar {
  margin-bottom: 20px;
}

.filter-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.filter-input {
  padding: 8px 14px;
  border: 1px solid #dde2eb;
  border-radius: 8px;
  font-size: 14px;
  outline: none;
  width: 220px;
  transition: border-color 0.2s;
}

.filter-input:focus {
  border-color: #4f7cff;
}

.filter-spacer {
  flex: 1;
}

/* ==================== 表格 ==================== */
.table-container {
  background: #ffffff;
  border-radius: 12px;
  border: 1px solid #e9edf4;
  overflow: hidden;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

.data-table th {
  background: #f8f9fc;
  color: #5a6a85;
  font-weight: 600;
  text-align: left;
  padding: 12px 16px;
  border-bottom: 1px solid #e9edf4;
  white-space: nowrap;
}

.data-table td {
  padding: 12px 16px;
  border-bottom: 1px solid #f0f3f8;
  color: #1e293b;
}

.data-table tbody tr:hover {
  background: #f8f9fc;
}

.empty-cell {
  text-align: center;
  color: #8b9ab0;
  padding: 40px 16px !important;
}

.text-bold {
  font-weight: 600;
}

.text-muted {
  color: #8b9ab0;
  font-size: 13px;
}

.action-cell {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.code-tag {
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: 12px;
  background: #f0f3f8;
  padding: 2px 8px;
  border-radius: 4px;
  color: #4a5568;
}

.code-tag.path {
  color: #4f7cff;
}

.status-badge {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 600;
}

.status-badge.active {
  background: #e8f5e9;
  color: #2e7d32;
}

.status-badge.inactive {
  background: #fce4e4;
  color: #c62828;
}

.method-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 700;
  font-family: 'Consolas', 'Monaco', monospace;
}

.method-badge.get { background: #e8f5e9; color: #2e7d32; }
.method-badge.post { background: #e3f2fd; color: #1565c0; }
.method-badge.put { background: #fff3e0; color: #e65100; }
.method-badge.delete { background: #fce4ec; color: #c62828; }
.method-badge.patch { background: #f3e5f5; color: #7b1fa2; }

.loading-state {
  padding: 60px;
  text-align: center;
  color: #8b9ab0;
  font-size: 15px;
}

/* ==================== 分页 ==================== */
.pagination-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 20px;
  padding: 12px 16px;
  background: #ffffff;
  border-radius: 10px;
  border: 1px solid #e9edf4;
}

.page-info {
  color: #5a6a85;
  font-size: 14px;
}

.page-btns {
  display: flex;
  gap: 6px;
}

/* ==================== 按钮 ==================== */
.btn {
  padding: 8px 16px;
  border-radius: 8px;
  border: none;
  font-size: 14px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 500;
  transition: all 0.2s;
}

.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.btn-primary {
  background: #4f7cff;
  color: #ffffff;
}

.btn-primary:hover:not(:disabled) {
  background: #3b5fd9;
}

.btn-outline {
  background: #ffffff;
  color: #4a5568;
  border: 1px solid #dde2eb;
}

.btn-outline:hover:not(:disabled) {
  background: #f0f3f8;
}

.btn-danger {
  background: #e74c3c;
  color: #ffffff;
}

.btn-danger:hover:not(:disabled) {
  background: #c0392b;
}

.btn-sm {
  padding: 5px 10px;
  font-size: 12px;
  border-radius: 6px;
}

/* ==================== 弹窗 ==================== */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  animation: fadeIn 0.2s ease;
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.modal-box {
  background: #ffffff;
  border-radius: 14px;
  width: 640px;
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
}

.modal-sm {
  width: 460px;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20px 24px 16px;
  border-bottom: 1px solid #e9edf4;
}

.modal-header h3 {
  font-size: 17px;
  font-weight: 600;
  color: #0b1a33;
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
}

.modal-close {
  background: none;
  border: none;
  font-size: 24px;
  color: #8b9ab0;
  cursor: pointer;
  padding: 0;
  line-height: 1;
}

.modal-close:hover {
  color: #e74c3c;
}

.modal-body {
  padding: 20px 24px;
}

.modal-desc {
  margin: 0 0 16px;
  color: #4a5568;
  font-size: 14px;
  line-height: 1.6;
}

.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 16px 24px;
  border-top: 1px solid #e9edf4;
}

/* ==================== 表单 ==================== */
.form-group {
  margin-bottom: 16px;
}

.form-group label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: #374151;
  margin-bottom: 6px;
}

.required {
  color: #e74c3c;
}

.form-input {
  width: 100%;
  padding: 9px 14px;
  border: 1px solid #dde2eb;
  border-radius: 8px;
  font-size: 14px;
  outline: none;
  transition: border-color 0.2s;
  box-sizing: border-box;
}

.form-input:focus {
  border-color: #4f7cff;
  box-shadow: 0 0 0 3px rgba(79, 124, 255, 0.1);
}

.form-row {
  display: flex;
  gap: 14px;
}

.form-row .form-group {
  flex: 1;
}

/* ==================== 权限选择网格 ==================== */
.perm-grid {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 400px;
  overflow-y: auto;
}

.perm-item {
  border: 1px solid #e9edf4;
  border-radius: 8px;
  transition: all 0.15s;
  cursor: pointer;
}

.perm-item:hover {
  border-color: #4f7cff;
  background: #fafcff;
}

.perm-item.checked {
  border-color: #4f7cff;
  background: #eef2ff;
}

.perm-check {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 14px;
}

.perm-check i {
  font-size: 18px;
  color: #4f7cff;
  margin-top: 2px;
}

.perm-info {
  flex: 1;
  min-width: 0;
}

.perm-name {
  font-size: 14px;
  font-weight: 600;
  color: #1e293b;
  margin-bottom: 4px;
}

.perm-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* ==================== 权限分组 ==================== */
.group-header-row {
  background: #fafcff;
}

.group-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0;
  font-weight: 600;
  font-size: 13px;
  color: #374151;
}

.group-header.clickable {
  cursor: pointer;
  user-select: none;
}

.group-header.clickable:hover {
  color: #4f7cff;
}

.group-header i {
  color: #4f7cff;
  font-size: 14px;
}

.group-badge {
  font-size: 11px;
  padding: 1px 8px;
  border-radius: 10px;
  font-weight: 500;
}

.group-badge.biz {
  background: #e8f5e9;
  color: #2e7d32;
}

.group-badge.sys {
  background: #f3e5f5;
  color: #7b1fa2;
}

.group-badge.small {
  font-size: 10px;
  padding: 1px 6px;
}

.group-count {
  font-size: 12px;
  color: #8b9ab0;
  font-weight: 400;
  margin-left: auto;
}

.group-header-row.sub {
  background: #f0f3f8;
}

.group-header-row.sys-toggle {
  cursor: pointer;
  background: #faf8fc;
  transition: background 0.15s;
}

.group-header-row.sys-toggle:hover {
  background: #f3eaf8;
}

.perm-group-label {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 0 4px 0;
  font-weight: 600;
  font-size: 13px;
  color: #374151;
  border-bottom: 1px solid #f0f3f8;
  margin-bottom: 4px;
}

.perm-group-label i {
  color: #4f7cff;
}

.perm-group-label:first-child {
  padding-top: 0;
}

.perm-group-label.sys-toggle {
  cursor: pointer;
  color: #7b1fa2;
  transition: color 0.15s;
}

.perm-group-label.sys-toggle:hover {
  color: #4f7cff;
}

.perm-group-label.sub {
  font-weight: 500;
  color: #5a6a85;
  font-size: 12px;
}

.btn:disabled,
.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
</style>
