<template>
  <div class="user-management p-1 bg-gray-50 h-screen flex flex-col">
    <!-- 搜索表单和用户表格 -->
    <div class="bg-white rounded-lg shadow-sm border border-gray-100 flex-1 flex flex-col overflow-hidden">
      <!-- 搜索表单 -->
      <div class="px-4 py-2 border-b border-gray-100">
        <n-form
          ref="searchFormRef"
          :model="searchForm"
          label-placement="left"
          label-width="auto"
          class="search-form"
        >
          <n-grid :cols="7" :x-gap="8" :y-gap="4">
            <n-grid-item>
              <n-form-item label="登录账号" path="loginName">
                <n-input
                  v-model:value="searchForm.loginName"
                  placeholder="请输入登录账号"
                  clearable
                  @keydown.enter="handleSearch"
                />
              </n-form-item>
            </n-grid-item>

            <n-grid-item>
              <n-form-item label="用户姓名" path="userName">
                <n-input
                  v-model:value="searchForm.userName"
                  placeholder="请输入用户姓名"
                  clearable
                  @keydown.enter="handleSearch"
                />
              </n-form-item>
            </n-grid-item>

            <n-grid-item>
              <n-form-item label="手机号" path="phone">
                <n-input
                  v-model:value="searchForm.phone"
                  placeholder="请输入手机号"
                  clearable
                  @keydown.enter="handleSearch"
                />
              </n-form-item>
            </n-grid-item>

            <n-grid-item>
              <n-form-item label="创建时间" path="timeRange">
                <n-date-picker
                  v-model:value="searchForm.timeRange"
                  type="datetimerange"
                  clearable
                  placeholder="选择时间范围"
                  :shortcuts="{
                    今天: () => [new Date().setHours(0, 0, 0, 0), new Date().setHours(23, 59, 59, 999)],
                    昨天: () => {
                      const yesterday = new Date();
                      yesterday.setDate(yesterday.getDate() - 1);
                      return [yesterday.setHours(0, 0, 0, 0), yesterday.setHours(23, 59, 59, 999)];
                    },
                    最近7天: () => [Date.now() - 7 * 24 * 60 * 60 * 1000, Date.now()],
                    最近30天: () => [Date.now() - 30 * 24 * 60 * 60 * 1000, Date.now()],
                  }"
                />
              </n-form-item>
            </n-grid-item>

            <n-grid-item>
              <n-form-item label="用户状态" path="userStatus">
                <n-select
                  v-model:value="searchForm.userStatus"
                  placeholder="请选择用户状态"
                  clearable
                  :options="[
                    { label: '启用', value: '0' },
                    { label: '停用', value: '1' },
                  ]"
                />
              </n-form-item>
            </n-grid-item>

            <n-grid-item>
              <n-form-item label="" path="">
                <NSpace>
                  <NButton type="primary" @click="handleSearch">
                    <template #icon>
                      <NIcon><icon-park-outline:search /></NIcon>
                    </template>
                    搜索
                  </NButton>
                  <NButton @click="handleReset">
                    <template #icon>
                      <NIcon><icon-park-outline:refresh /></NIcon>
                    </template>
                    重置
                  </NButton>
                </NSpace>
              </n-form-item>
            </n-grid-item>
          </n-grid>
        </n-form>
      </div>
      <!-- 表格头部操作栏 -->
      <div class="flex items-center justify-between px-4 py-2 border-b border-gray-100">
        <div class="flex items-center gap-4">
          <NButton v-button="PERMISSIONS.USER.ADD" type="primary" class="px-6 flex items-center" @click="handleAdd">
            <template #icon>
              <NIcon class="mr-1" style="transform: translateY(-1px)">
                <icon-park-outline:plus />
              </NIcon>
            </template>
            新增
          </NButton>

          <NButton
            v-button="PERMISSIONS.USER.DELETE"
            type="error"
            :disabled="selectedRows.length === 0"
            class="px-3 flex items-center"
            @click="handleBatchDelete"
          >
            <template #icon>
              <NIcon class="mr-1" style="transform: translateY(-1px)">
                <icon-park-outline:delete />
              </NIcon>
            </template>
            删除
          </NButton>

          <NButton
            v-button="PERMISSIONS.USER.ROLE"
            type="info"
            :disabled="selectedRows.length !== 1"
            class="px-3 flex items-center"
            @click="handleAssignRole(selectedRows[0])"
          >
            <template #icon>
              <NIcon class="mr-1" style="transform: translateY(-1px)">
                <icon-park-outline:userPositioning />
              </NIcon>
            </template>
            分配角色
          </NButton>

          <NDropdown
            v-button="PERMISSIONS.USER.EXPORT"
            trigger="click"
            :show="showExportDropdown"
            :options="[
              {
                label: '导出当前查询数据',
                key: 'current',
                icon: () => h(NIcon, null, { default: () => h('icon-park-outline:download') }),
              },
              {
                label: '导出全部数据',
                key: 'all',
                icon: () => h(NIcon, null, { default: () => h('icon-park-outline:download-one') }),
              },
              {
                type: 'divider',
              },
              {
                label: '下载导入模板',
                key: 'template',
                icon: () => h(NIcon, null, { default: () => h('icon-park-outline:file-code-one') }),
              },
            ]"
            @update:show="(show: boolean) => showExportDropdown = show"
            @select="handleExportMenuSelect"
          >
            <NButton class="px-6 flex items-center">
              <template #icon>
                <NIcon class="mr-1" style="transform: translateY(-1px)">
                  <icon-park-outline:download />
                </NIcon>
              </template>
              导出
              <NIcon class="ml-1" style="transform: translateY(-1px)">
                <icon-park-outline:down />
              </NIcon>
            </NButton>
          </NDropdown>

          <NButton v-button="PERMISSIONS.USER.IMPORT" class="px-6 flex items-center" @click="handleImport">
            <template #icon>
              <NIcon class="mr-1" style="transform: translateY(-1px)">
                <icon-park-outline:upload />
              </NIcon>
            </template>
            导入
          </NButton>
        </div>

        <div class="flex items-center gap-4 text-sm text-gray-500">
          <span>共 {{ pagination.itemCount }} 条</span>
          <NButton text @click="getUserList">
            <template #icon>
              <NIcon><icon-park-outline:refresh /></NIcon>
            </template>
          </NButton>
        </div>
      </div>

      <!-- 表格内容 -->
      <div class="table-wrapper flex-1 overflow-auto pt-4">
        <!-- 数据表格 -->
        <n-data-table
          v-if="tableData.length > 0 || loading"
          :columns="columns"
          :data="tableData"
          :loading="loading"
          :row-key="(row: UserVo) => row.userId"
          :bordered="false"
          :single-line="false"
          size="large"
          class="custom-table"
          @update:checked-row-keys="handleRowSelectionChange"
        />

        <!-- 空状态 -->
        <CoiEmpty
          v-else
          :type="getEmptyType()"
          :title="getEmptyTitle()"
          :description="getEmptyDescription()"
          :show-action="true"
          :action-text="getEmptyActionText()"
          size="medium"
          @action="handleEmptyAction"
        >
          <template #action>
            <NButton
              type="primary"
              size="medium"
              round
              class="coi-empty__action-btn"
              @click="handleEmptyAction"
            >
              <template #icon>
                <NIcon>
                  <icon-park-outline:refresh v-if="hasSearchConditions()" />
                  <icon-park-outline:plus v-else />
                </NIcon>
              </template>
              {{ getEmptyActionText() }}
            </NButton>
          </template>
        </CoiEmpty>
      </div>

      <!-- 分页器 -->
      <div v-if="tableData.length > 0" class="flex items-center px-4 py-2 border-t border-gray-100">
        <div class="text-sm text-gray-500 mr-4">
          共 {{ pagination.itemCount }} 条
        </div>
        <n-pagination
          v-model:page="pagination.page"
          v-model:page-size="pagination.pageSize"
          :item-count="pagination.itemCount"
          :show-size-picker="true"
          :page-sizes="[10, 20, 50, 100]"
          show-quick-jumper
          @update:page="handlePageChange"
          @update:page-size="handlePageSizeChange"
        />
      </div>
    </div>

    <!-- 用户表单弹框 -->
    <CoiDialog
      ref="userDialogRef"
      :title="modalTitle"
      :width="800"
      height="auto"
      confirm-text="确定"
      cancel-text="取消"
      @coi-confirm="handleSubmit"
      @coi-cancel="handleCancel"
    >
      <template #content>
        <div class="px-3 py-2">
          <n-form
            ref="formRef"
            :model="formData"
            :rules="rules"
            label-placement="left"
            label-width="100px"
            require-mark-placement="right-hanging"
          >
            <n-grid :cols="2" :x-gap="24">
              <n-grid-item>
                <n-form-item label="登录账号" path="loginName">
                  <n-input
                    v-model:value="formData.loginName"
                    placeholder="请输入登录账号"
                    :disabled="isEdit"
                  />
                </n-form-item>
              </n-grid-item>

              <n-grid-item>
                <n-form-item label="用户姓名" path="userName">
                  <n-input
                    v-model:value="formData.userName"
                    placeholder="请输入用户姓名"
                  />
                </n-form-item>
              </n-grid-item>

              <n-grid-item>
                <n-form-item label="用户类型" path="userType">
                  <n-select
                    v-model:value="formData.userType"
                    placeholder="请选择用户类型"
                    :options="[
                      { label: '系统用户', value: '1' },
                      { label: '注册用户', value: '2' },
                      { label: '微信用户', value: '3' },
                    ]"
                  />
                </n-form-item>
              </n-grid-item>

              <n-grid-item>
                <n-form-item label="性别" path="sex">
                  <n-select
                    v-model:value="formData.sex"
                    placeholder="请选择性别"
                    :options="[
                      { label: '男', value: '1' },
                      { label: '女', value: '2' },
                      { label: '未知', value: '3' },
                    ]"
                  />
                </n-form-item>
              </n-grid-item>

              <n-grid-item>
                <n-form-item label="邮箱" path="email">
                  <n-input
                    v-model:value="formData.email"
                    placeholder="请输入邮箱"
                  />
                </n-form-item>
              </n-grid-item>

              <n-grid-item>
                <n-form-item label="手机号" path="phone">
                  <n-input
                    v-model:value="formData.phone"
                    placeholder="请输入手机号"
                  />
                </n-form-item>
              </n-grid-item>

              <n-grid-item v-if="!isEdit">
                <n-form-item label="密码" path="password">
                  <n-input
                    v-model:value="formData.password"
                    type="password"
                    placeholder="请输入密码"
                    show-password-on="click"
                  />
                </n-form-item>
              </n-grid-item>

              <n-grid-item>
                <n-form-item label="用户状态" path="userStatus">
                  <n-radio-group v-model:value="formData.userStatus">
                    <n-radio value="0">
                      启用
                    </n-radio>
                    <n-radio value="1">
                      停用
                    </n-radio>
                  </n-radio-group>
                </n-form-item>
              </n-grid-item>

              <n-grid-item v-if="roleOptions.length > 0" :span="2">
                <n-form-item label="角色分配" path="roleIds">
                  <n-select
                    v-model:value="formData.roleIds"
                    multiple
                    placeholder="请选择角色"
                    class="role-select"
                    :render-tag="renderRoleTag"
                    :options="roleOptions.map(role => ({
                      label: role.roleName,
                      value: role.roleId,
                    }))"
                  />
                </n-form-item>
              </n-grid-item>
            </n-grid>

            <n-form-item label="备注" path="remark">
              <n-input
                v-model:value="formData.remark"
                type="textarea"
                placeholder="请输入备注信息"
                :rows="3"
              />
            </n-form-item>
          </n-form>
        </div>
      </template>
    </CoiDialog>

    <!-- 角色分配弹框 -->
    <CoiDialog
      ref="roleDialogRef"
      :title="roleModalTitle"
      :width="600"
      height="auto"
      confirm-text="确定"
      cancel-text="取消"
      @coi-confirm="handleConfirmAssignRole"
      @coi-cancel="handleCancelAssignRole"
    >
      <template #content>
        <div class="p-3 space-y-4">
          <div class="bg-gray-50 p-4 rounded-lg">
            <div class="flex items-center gap-3">
              <div class="w-10 h-10 rounded-full bg-gradient-to-r from-blue-400 to-purple-500 flex items-center justify-center text-white text-sm font-bold">
                {{ currentAssignUser?.loginName?.charAt(0).toUpperCase() }}
              </div>
              <div>
                <div class="font-medium text-gray-900">
                  {{ currentAssignUser?.userName }}
                </div>
                <div class="text-sm text-gray-500">
                  {{ currentAssignUser?.loginName }}
                </div>
              </div>
            </div>
          </div>

          <div>
            <label class="block text-sm font-medium text-gray-700 mb-2">选择角色</label>
            <div v-if="roleLoading" class="text-center text-gray-500 p-8">
              <n-spin size="small" />
              <p class="mt-2">
                正在加载角色列表...
              </p>
            </div>
            <n-transfer
              v-else-if="availableRoles.length > 0"
              v-model:value="selectedRoleIds"
              :options="availableRoles"
              source-title="可选角色"
              target-title="已选角色"
              :filterable="true"
              :show-selected="true"
              class="role-transfer"
            />
            <div v-else class="text-center text-gray-500 p-8">
              <div class="text-2xl text-gray-400 mb-2">
                ℹ️
              </div>
              <p class="mt-2">
                暂无可分配的角色
              </p>
            </div>
          </div>
        </div>
      </template>
    </CoiDialog>

    <!-- 重置密码弹框 -->
    <CoiDialog
      ref="resetPwdDialogRef"
      title="重置用户密码"
      :width="500"
      height="auto"
      confirm-text="确认重置"
      cancel-text="取消"
      @coi-confirm="handleConfirmResetPassword"
      @coi-cancel="handleCancelResetPassword"
    >
      <template #content>
        <div class="p-3 space-y-4">
          <div class="bg-gray-50 p-4 rounded-lg">
            <div class="flex items-center gap-3">
              <div class="w-10 h-10 rounded-full bg-gradient-to-r from-orange-400 to-red-500 flex items-center justify-center text-white text-sm font-bold">
                {{ currentResetUser?.loginName?.charAt(0).toUpperCase() }}
              </div>
              <div>
                <div class="font-medium text-gray-900">
                  {{ currentResetUser?.userName }}
                </div>
                <div class="text-sm text-gray-500">
                  {{ currentResetUser?.loginName }}
                </div>
              </div>
            </div>
          </div>

          <n-form
            ref="resetPwdFormRef"
            :model="resetPwdForm"
            :rules="resetPwdRules"
            label-placement="left"
            label-width="80px"
            require-mark-placement="right-hanging"
          >
            <n-form-item label="新密码" path="newPassword">
              <n-input
                v-model:value="resetPwdForm.newPassword"
                type="password"
                placeholder="请输入新密码(最少6位)"
                show-password-on="click"
              />
            </n-form-item>

            <n-form-item label="确认密码" path="confirmPassword">
              <n-input
                v-model:value="resetPwdForm.confirmPassword"
                type="password"
                placeholder="请再次输入新密码"
                show-password-on="click"
              />
            </n-form-item>
          </n-form>

          <div class="bg-blue-50 border border-blue-200 rounded-lg p-3">
            <div class="flex items-start gap-2">
              <div class="text-blue-500 text-sm mt-0.5">
                ℹ️
              </div>
              <div class="text-blue-700 text-sm">
                <p class="font-medium mb-1">
                  温馨提示：
                </p>
                <ul class="list-disc list-inside space-y-1 text-xs">
                  <li>密码长度至少6位字符</li>
                  <li>重置后用户需要使用新密码登录</li>
                  <li>建议提醒用户及时修改密码</li>
                </ul>
              </div>
            </div>
          </div>
        </div>
      </template>
    </CoiDialog>

    <!-- 头像查看弹框 -->
    <CoiDialog
      ref="avatarDialogRef"
      :width="600"
      height="auto"
      cancel-text="关闭"
      :show-confirm="false"
      @coi-cancel="handleCloseAvatar"
    >
      <template #header>
        <div class="flex items-center gap-3">
          <div class="w-8 h-8 rounded-full bg-gradient-to-r from-purple-400 to-pink-400 flex items-center justify-center text-white text-sm font-bold">
            {{ currentAvatarUser?.loginName?.charAt(0).toUpperCase() }}
          </div>
          <div>
            <span class="text-lg font-semibold">{{ currentAvatarUser?.userName }}</span>
            <span class="text-sm text-gray-500 ml-2">({{ currentAvatarUser?.loginName }})</span>
          </div>
        </div>
      </template>

      <template #content>
        <div class="flex flex-col items-center space-y-4 p-3">
          <div class="relative">
            <img
              :src="currentAvatar"
              :alt="`${currentAvatarUser?.userName}的头像`"
              class="w-80 h-80 object-cover rounded-lg shadow-lg border-4 border-gray-100"
              @error="handleAvatarError"
            >
            <div class="absolute -bottom-2 -right-2 bg-white rounded-full p-2 shadow-lg">
              <div class="w-3 h-3 bg-green-500 rounded-full" />
            </div>
          </div>

          <div class="text-center space-y-2">
            <p class="text-gray-600 text-sm">
              {{ currentAvatarUser?.avatar ? '用户头像' : '默认头像' }}
            </p>
            <div class="flex items-center justify-center gap-4 text-xs text-gray-400">
              <span>用户类型: {{
                currentAvatarUser?.userType === '1' ? '系统用户'
                : currentAvatarUser?.userType === '2' ? '注册用户' : '微信用户'
              }}</span>
              <span>状态: {{ currentAvatarUser?.userStatus === '0' ? '启用' : '停用' }}</span>
            </div>
          </div>
        </div>
      </template>
    </CoiDialog>

    <!-- 导入用户弹框 -->
    <CoiDialog
      ref="importDialogRef"
      title="导入用户数据"
      :width="600"
      height="auto"
      confirm-text="开始导入"
      cancel-text="取消"
      @coi-confirm="handleConfirmImport"
      @coi-cancel="handleCancelImport"
    >
      <template #content>
        <div class="p-3 space-y-6">
          <!-- 文件上传区域 -->
          <div>
            <h4 class="text-sm font-medium text-gray-700 mb-3">
              选择Excel文件
            </h4>
            <NUpload
              :default-file-list="[]"
              :max="1"
              accept=".xlsx,.xls,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
              :on-before-upload="handleFileSelect"
              :show-file-list="true"
              :disabled="importLoading"
            >
              <NUploadDragger>
                <div class="text-center">
                  <NIcon size="48" :depth="3" class="mb-2">
                    <icon-park-outline:file-excel />
                  </NIcon>
                  <div class="text-lg font-medium mb-1">
                    点击或拖拽上传Excel文件
                  </div>
                  <div class="text-sm text-gray-500">
                    支持 .xlsx 和 .xls 格式，文件大小不超过 10MB
                  </div>
                </div>
              </NUploadDragger>
            </NUpload>
          </div>

          <!-- 导入选项 -->
          <div>
            <h4 class="text-sm font-medium text-gray-700 mb-3">
              导入选项
            </h4>
            <NCheckbox
              v-model:checked="updateSupport"
              :disabled="importLoading"
            >
              覆盖已存在的用户数据
            </NCheckbox>
            <div class="text-xs text-gray-500 mt-1">
              勾选后，如果导入的用户登录名已存在，将更新该用户的信息
            </div>
          </div>

          <!-- 进度条 -->
          <div v-if="importLoading">
            <h4 class="text-sm font-medium text-gray-700 mb-3">
              导入进度
            </h4>
            <NProgress
              type="line"
              :percentage="uploadProgress"
              :show-indicator="true"
              processing
            />
            <div class="text-sm text-gray-500 mt-2 text-center">
              正在导入用户数据，请稍候...
            </div>
          </div>

          <!-- 说明 -->
          <div class="text-xs text-gray-500 bg-gray-50 p-3 rounded">
            <div class="font-medium mb-2">
              导入说明：
            </div>
            <ul class="space-y-1">
              <li>• 请使用系统提供的模板格式进行导入</li>
              <li>• 登录账号为必填字段，且不能重复</li>
              <li>• 导入的用户默认密码为 "123456"</li>
              <li>• 用户状态默认为"启用"</li>
            </ul>
          </div>

          <!-- 下载模板按钮 -->
          <div class="flex justify-start">
            <NButton
              :disabled="importLoading"
              @click="handleDownloadTemplate"
            >
              <template #icon>
                <NIcon><icon-park-outline:download /></NIcon>
              </template>
              下载模板
            </NButton>
          </div>
        </div>
      </template>
    </CoiDialog>
  </div>
</template>

<script setup lang="ts">
import { h, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import type { DataTableColumns, FormInst } from 'naive-ui'
import { NButton, NCheckbox, NDropdown, NIcon, NPopconfirm, NProgress, NSpace, NSwitch, NTag, NTooltip, NUpload, NUploadDragger } from 'naive-ui'
import IconParkOutlineEditOne from '~icons/icon-park-outline/edit-one'
import IconParkOutlineDelete from '~icons/icon-park-outline/delete'
import IconParkOutlineRefresh from '~icons/icon-park-outline/refresh'
import IconParkOutlineUserPositioning from '~icons/icon-park-outline/user-positioning'
import CoiDialog from '@/components/common/CoiDialog.vue'
import CoiEmpty from '@/components/common/CoiEmpty.vue'
import {
  addUser,
  batchDeleteUsers,
  deleteUser,
  downloadExcelTemplate,
  exportExcelData,
  fetchUserPage,
  getUserById,
  importUserData,
  resetUserPassword,
  updateUser,
  updateUserStatus,
} from '@/service/api/system/user'
import type { UserSearchForm, UserVo } from '@/service/api/system/user'

import {
  assignUserRole,
  fetchNormalRoleForUser,
  fetchRoleList,

} from '@/service/api/system/role'
import type { RoleVo } from '@/service/api/system/role'
import { coiMsgBox, coiMsgError, coiMsgInfo, coiMsgSuccess, coiMsgWarning } from '@/utils/coi'
import { PERMISSIONS } from '@/constants/permissions'
import { usePermission } from '@/hooks/usePermission'

// 权限相关
const { hasButton } = usePermission()

// 响应式数据
const loading = ref(false)
const tableData = ref<UserVo[]>([])
const modalTitle = ref('新增用户')
const formRef = ref<FormInst | null>(null)
const searchFormRef = ref<FormInst | null>(null)
const isEdit = ref(false)
const currentUser = ref<UserVo | null>(null)
const selectedRows = ref<UserVo[]>([])
const roleOptions = ref<RoleVo[]>([])

// 弹框引用
const userDialogRef = ref()
const roleDialogRef = ref()
const resetPwdDialogRef = ref()
const avatarDialogRef = ref()
const importDialogRef = ref()

// 角色分配相关
const roleModalTitle = ref('分配角色')
const currentAssignUser = ref<UserVo | null>(null)
const availableRoles = ref<{ label: string, value: number }[]>([])
const selectedRoleIds = ref<number[]>([])
const roleLoading = ref(false)

// 重置密码相关
const resetPwdFormRef = ref<FormInst | null>(null)
const currentResetUser = ref<UserVo | null>(null)
const resetPwdForm = ref({
  newPassword: '',
  confirmPassword: '',
})

// 头像查看相关
const currentAvatar = ref('')
const currentAvatarUser = ref<UserVo | null>(null)

// 导入相关
const importLoading = ref(false)
const selectedFile = ref<File | null>(null)
const updateSupport = ref(false)
const uploadProgress = ref(0)

const progressInterval = ref<NodeJS.Timeout | null>(null)
const createdBlobUrls = ref<string[]>([])

// 分页数据
const pagination = ref({
  page: 1,
  pageSize: 10,
  itemCount: 0,
  showSizePicker: true,
  pageSizes: [10, 20, 50, 100],
})

// 搜索表单数据
const searchForm = ref<UserSearchForm>({})

// 表单数据
const formData = ref({
  loginName: '',
  userName: '',
  password: '',
  confirmPassword: '',
  userType: '1',
  email: '',
  phone: '',
  sex: '3',
  userStatus: '0',
  remark: '',
  roleIds: [] as number[],
})

// 表单验证规则
const rules = {
  loginName: [
    { required: true, message: '请输入登录账号', trigger: 'blur' },
    { min: 3, max: 16, message: '账号长度为 3-16 位', trigger: 'blur' },
    { pattern: /^[a-z0-9]+$/i, message: '账号格式为数字以及字母', trigger: 'blur' },
  ],
  userName: [
    { required: true, message: '请输入用户姓名', trigger: 'blur' },
  ],
  email: [
    { type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' },
  ],
  phone: [
    { required: true, message: '请输入手机号', trigger: 'blur' },
    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' },
  ],
  password: [
    { required: true, message: '请输入密码', trigger: 'blur' },
    { min: 6, message: '密码长度不能少于6位', trigger: 'blur' },
  ],
  userType: [
    { required: true, message: '请选择用户类型', trigger: 'change' },
  ],
  userStatus: [
    { required: true, message: '请选择用户状态', trigger: 'change' },
  ],
}

// 重置密码表单验证规则
const resetPwdRules = {
  newPassword: [
    { required: true, message: '请输入新密码', trigger: 'blur' },
    { min: 6, message: '密码长度不能少于6位', trigger: 'blur' },
  ],
  confirmPassword: [
    { required: true, message: '请确认密码', trigger: 'blur' },
    {
      validator: (_rule: any, value: string) => {
        if (value && value !== resetPwdForm.value.newPassword) {
          return new Error('两次输入的密码不一致')
        }
        return true
      },
      trigger: 'blur',
    },
  ],
}

// 表格列定义
const columns: DataTableColumns<UserVo> = [
  {
    type: 'selection',
    width: 50,
  },
  {
    title: '序号',
    key: 'index',
    width: 70,
    align: 'center',
    render: (_, index) => {
      return (pagination.value.page - 1) * pagination.value.pageSize + index + 1
    },
  },
  {
    title: '头像',
    key: 'avatar',
    width: 80,
    align: 'center',
    render: (row) => {
      return h('div', { class: 'flex justify-center' }, [
        h('div', {
          class: 'w-10 h-10 rounded-full cursor-pointer hover:scale-110 transition-transform duration-200 overflow-hidden border-2 border-gray-200 hover:border-blue-400',
          onClick: () => handleViewAvatar(row),
        }, [
          row.avatar
            ? h('img', {
                src: row.avatar,
                alt: `${row.userName}的头像`,
                class: 'w-full h-full object-cover',
                onError: (e) => {
                // 头像加载失败时显示默认头像
                  (e.target as HTMLElement).style.display = 'none'
                  const parent = (e.target as HTMLElement).parentElement
                  if (parent) {
                    parent.className = 'w-10 h-10 rounded-full bg-gradient-to-r from-blue-400 to-purple-500 flex items-center justify-center text-white text-sm font-bold shadow-md cursor-pointer hover:scale-110 transition-transform duration-200'
                    parent.textContent = row.loginName.charAt(0).toUpperCase()
                  }
                },
              })
            : h('div', {
                class: 'w-full h-full bg-gradient-to-r from-blue-400 to-purple-500 flex items-center justify-center text-white text-sm font-bold',
              }, row.loginName.charAt(0).toUpperCase()),
        ]),
      ])
    },
  },
  {
    title: '登录账号',
    key: 'loginName',
    width: 120,
    align: 'center',
    render: (row) => {
      return h('span', { class: 'text-gray-600' }, row.loginName)
    },
  },
  {
    title: '用户姓名',
    key: 'userName',
    width: 120,
    align: 'center',
    render: (row) => {
      return h('div', { class: 'text-gray-600' }, row.userName)
    },
  },
  {
    title: '邮箱',
    key: 'email',
    width: 120,
    align: 'center',
    ellipsis: { tooltip: true },
    render: (row) => {
      return h('span', { class: 'email-cell-text' }, row.email || '-')
    },
  },
  {
    title: '手机号',
    key: 'phone',
    align: 'center',
    width: 130,
    render: (row) => {
      return h('span', { class: 'text-gray-600' }, row.phone || '-')
    },
  },
  {
    title: '用户类型',
    key: 'userType',
    width: 100,
    align: 'center',
    render: (row) => {
      const typeMap: Record<string, { label: string, type: any }> = {
        1: { label: '系统用户', type: 'primary' },
        2: { label: '注册用户', type: 'info' },
        3: { label: '微信用户', type: 'warning' },
      }
      const config = typeMap[row.userType] || { label: '未知', type: 'default' }
      return h(NTag, { type: config.type, size: 'small' }, { default: () => config.label })
    },
  },
  {
    title: '性别',
    key: 'sex',
    width: 80,
    align: 'center',
    render: (row) => {
      const sexMap: Record<string, { label: string, icon: string, color: string }> = {
        1: { label: '男', icon: '♂', color: 'text-blue-500' },
        2: { label: '女', icon: '♀', color: 'text-pink-500' },
        3: { label: '未知', icon: '?', color: 'text-gray-400' },
      }
      const config = sexMap[row.sex || '3']
      return h('div', { class: `flex items-center justify-center gap-1 ${config.color}` }, [
        h('span', { class: 'text-lg' }, config.icon),
        h('span', { class: 'text-xs' }, config.label),
      ])
    },
  },
  {
    title: '用户状态',
    key: 'userStatus',
    width: 100,
    align: 'center',
    render: (row) => {
      return h('div', { class: 'flex items-center justify-center' }, [
        h(NPopconfirm, {
          onPositiveClick: () => handleToggleStatus(row),
          negativeText: '取消',
          positiveText: '确定',
        }, {
          default: () => `确定要${row.userStatus === '0' ? '停用' : '启用'}用户「${row.userName}」吗？`,
          trigger: () => h(NSwitch, {
            value: row.userStatus === '0',
            size: 'small',
            checkedChildren: '启用',
            uncheckedChildren: '停用',
            loading: false,
          }),
        }),
      ])
    },
  },
  {
    title: '登录时间',
    key: 'loginTime',
    width: 160,
    align: 'center',
    render: (row) => {
      return h('span', { class: 'text-gray-500 text-sm' }, row.loginTime || '-')
    },
  },
  {
    title: '操作',
    key: 'actions',
    width: 160,
    align: 'center',
    fixed: 'right',
    render: (row) => {
      const buttons = []

      // 编辑按钮
      if (hasButton(PERMISSIONS.USER.UPDATE)) {
        buttons.push(h(NTooltip, {
          trigger: 'hover',
        }, {
          default: () => '编辑',
          trigger: () => h(NButton, {
            type: 'primary',
            size: 'medium',
            circle: true,
            class: 'action-btn action-btn-edit',
            onClick: () => handleEdit(row),
          }, {
            icon: () => h(NIcon, { size: 18 }, { default: () => h(IconParkOutlineEditOne) }),
          }),
        }))
      }

      // 删除按钮
      if (hasButton(PERMISSIONS.USER.DELETE)) {
        buttons.push(h(NPopconfirm, {
          onPositiveClick: () => handleDelete(row.userId),
          negativeText: '取消',
          positiveText: '确定',
        }, {
          default: () => '确定删除此用户吗？',
          trigger: () => h(NTooltip, {
            trigger: 'hover',
          }, {
            default: () => '删除',
            trigger: () => h(NButton, {
              type: 'error',
              size: 'medium',
              circle: true,
              class: 'action-btn action-btn-delete',
            }, {
              icon: () => h(NIcon, { size: 18 }, { default: () => h(IconParkOutlineDelete) }),
            }),
          }),
        }))
      }

      // 重置密码按钮
      if (hasButton(PERMISSIONS.USER.RESET_PWD)) {
        buttons.push(h(NTooltip, {
          trigger: 'hover',
        }, {
          default: () => '重置密码',
          trigger: () => h(NButton, {
            type: 'warning',
            size: 'medium',
            circle: true,
            class: 'action-btn action-btn-warning',
            onClick: () => handleResetPassword(row),
          }, {
            icon: () => h(NIcon, { size: 18 }, { default: () => h(IconParkOutlineRefresh) }),
          }),
        }))
      }

      // 分配角色按钮
      if (hasButton(PERMISSIONS.USER.ROLE)) {
        buttons.push(h(NTooltip, {
          trigger: 'hover',
        }, {
          default: () => '分配角色',
          trigger: () => h(NButton, {
            type: 'warning',
            size: 'medium',
            circle: true,
            class: 'action-btn action-btn-warning',
            onClick: () => handleAssignRole(row),
          }, {
            icon: () => h(NIcon, { size: 18 }, { default: () => h(IconParkOutlineUserPositioning) }),
          }),
        }))
      }

      return h('div', { class: 'flex items-center justify-center gap-2' }, buttons)
    },
  },
]

// 获取用户列表
async function getUserList() {
  loading.value = true
  try {
    // 构建请求参数，处理时间范围
    const { timeRange, ...otherParams } = searchForm.value

    // 过滤掉空值和null值，只保留有效的搜索条件
    const filteredParams = Object.entries(otherParams).reduce((acc, [key, value]) => {
      if (value !== null && value !== undefined && value !== '') {
        acc[key] = value
      }
      return acc
    }, {} as Record<string, any>)

    const params = {
      pageNo: pagination.value.page,
      pageSize: pagination.value.pageSize,
      ...filteredParams,
    }

    // 处理时间范围，转换为beginTime和endTime
    if (timeRange && timeRange.length === 2) {
      params.beginTime = new Date(timeRange[0]).toISOString().slice(0, 19).replace('T', ' ')
      params.endTime = new Date(timeRange[1]).toISOString().slice(0, 19).replace('T', ' ')
    }

    const { isSuccess, data } = await fetchUserPage(params)

    if (isSuccess && data) {
      tableData.value = data.records || []
      pagination.value.itemCount = data.total || 0
    }
    else {
      coiMsgError('获取用户列表失败，请检查网络连接或联系管理员')
      tableData.value = []
      pagination.value.itemCount = 0
    }
  }
  catch {
    coiMsgError('获取用户列表失败，请检查网络连接')
    tableData.value = []
    pagination.value.itemCount = 0
  }
  finally {
    loading.value = false
  }
}

// 获取角色列表
async function getRoleList() {
  try {
    const { isSuccess, data } = await fetchRoleList()

    if (isSuccess && data) {
      roleOptions.value = data
    }
  }
  catch {
  }
}

// 分页变化处理
function handlePageChange(page: number) {
  pagination.value.page = page
  getUserList()
}

function handlePageSizeChange(pageSize: number) {
  pagination.value.pageSize = pageSize
  pagination.value.page = 1
  getUserList()
}

// 搜索
function handleSearch() {
  pagination.value.page = 1
  getUserList()
}

// 处理键盘事件 (暂时未使用，保留用于后续扩展)
// function handleKeydown(event: KeyboardEvent) {
//   if (event.key === 'Enter') {
//     handleSearch()
//   }
// }

// 重置搜索
async function handleReset() {
  // 重置搜索表单的所有字段 - 明确清空每个字段
  searchForm.value = {
    loginName: '',
    userName: '',
    phone: '',
    userStatus: null,
    beginTime: '',
    endTime: '',
    timeRange: null,
  }

  // 使用 nextTick 确保 DOM 更新后再重置表单状态
  await nextTick()

  // 重置表单验证状态
  if (searchFormRef.value) {
    searchFormRef.value.restoreValidation()
  }

  // 重置分页到第一页
  pagination.value.page = 1
  // 重新获取数据
  getUserList()
}

// 行选择变化
function handleRowSelectionChange(rowKeys: number[]) {
  selectedRows.value = tableData.value.filter(row => rowKeys.includes(row.userId))
}

// 新增用户
function handleAdd() {
  modalTitle.value = '新增用户'
  isEdit.value = false
  currentUser.value = null
  formData.value = {
    loginName: '',
    userName: '',
    password: '',
    confirmPassword: '',
    userType: '1',
    email: '',
    phone: '',
    sex: '3',
    userStatus: '0',
    remark: '',
    roleIds: [],
  }
  userDialogRef.value?.coiOpen()
}

// 编辑用户
async function handleEdit(user: UserVo) {
  modalTitle.value = '编辑用户'
  isEdit.value = true
  currentUser.value = user

  try {
    // 获取完整的用户信息（包含roleIds）
    const { isSuccess, data } = await getUserById(user.userId)

    if (isSuccess && data) {
      formData.value = {
        loginName: data.loginName,
        userName: data.userName,
        password: '',
        confirmPassword: '',
        userType: data.userType,
        email: data.email || '',
        phone: data.phone || '',
        sex: data.sex || '3',
        userStatus: data.userStatus,
        remark: data.remark || '',
        roleIds: data.roleIds || [],
      }
    }
    else {
      // 如果获取详情失败，使用列表数据作为备用
      formData.value = {
        loginName: user.loginName,
        userName: user.userName,
        password: '',
        confirmPassword: '',
        userType: user.userType,
        email: user.email || '',
        phone: user.phone || '',
        sex: user.sex || '3',
        userStatus: user.userStatus,
        remark: user.remark || '',
        roleIds: [],
      }
    }
  }
  catch {
    // 如果获取详情出错，使用列表数据作为备用
    formData.value = {
      loginName: user.loginName,
      userName: user.userName,
      password: '',
      confirmPassword: '',
      userType: user.userType,
      email: user.email || '',
      phone: user.phone || '',
      sex: user.sex || '3',
      userStatus: user.userStatus,
      remark: user.remark || '',
      roleIds: [],
    }
  }

  userDialogRef.value?.coiOpen()
}

// 删除用户
async function handleDelete(userId: number) {
  try {
    const { isSuccess } = await deleteUser(userId)
    if (isSuccess) {
      coiMsgSuccess('删除成功')
      await getUserList()
    }
    else {
      coiMsgError('删除失败')
    }
  }
  catch {
    coiMsgError('删除失败')
  }
}

// 批量删除用户
async function handleBatchDelete() {
  if (selectedRows.value.length === 0) {
    coiMsgWarning('请先选择要删除的用户')
    return
  }

  try {
    // 确认删除操作
    await coiMsgBox(`确定要删除选中的 ${selectedRows.value.length} 个用户吗？`, '批量删除确认')
  }
  catch {
    // 用户取消删除操作，直接返回，不显示任何提示
    return
  }

  // 用户确认删除，执行删除操作
  try {
    const userIds = selectedRows.value.map(user => user.userId)
    const { isSuccess } = await batchDeleteUsers(userIds)
    if (isSuccess) {
      coiMsgSuccess('批量删除成功')
      selectedRows.value = []
      await getUserList()
    }
    else {
      coiMsgError('批量删除失败')
    }
  }
  catch {
    coiMsgError('批量删除失败')
  }
}

// 切换用户状态
async function handleToggleStatus(user: UserVo) {
  try {
    const newStatus = user.userStatus === '0' ? '1' : '0'
    const statusText = newStatus === '0' ? '启用' : '停用'

    const { isSuccess } = await updateUserStatus(user.userId, newStatus)
    if (isSuccess) {
      user.userStatus = newStatus
      coiMsgSuccess(`用户「${user.userName}」${statusText}成功`)
      // 刷新用户列表以确保数据同步
      await getUserList()
    }
    else {
      coiMsgError(`用户${statusText}失败`)
    }
  }
  catch {
    coiMsgError('状态修改失败，请检查网络连接')
  }
}

// 重置密码
function handleResetPassword(user: UserVo) {
  currentResetUser.value = user
  resetPwdForm.value = {
    newPassword: '',
    confirmPassword: '',
  }
  resetPwdDialogRef.value?.coiOpen()
}

// 执行重置密码
async function handleConfirmResetPassword() {
  if (!resetPwdFormRef.value || !currentResetUser.value) {
    return
  }

  try {
    // 先进行表单验证
    await resetPwdFormRef.value.validate()
  }
  catch {
    // 表单验证失败，提示用户检查填写内容
    coiMsgWarning('验证失败，请检查填写内容')
    return
  }

  // 表单验证通过，执行API调用
  try {
    const { isSuccess } = await resetUserPassword(currentResetUser.value.userId, resetPwdForm.value.newPassword)
    if (isSuccess) {
      coiMsgSuccess(`用户「${currentResetUser.value.userName}」密码重置成功`)
      resetPwdDialogRef.value?.coiClose()
    }
    else {
      coiMsgError('重置密码失败，请稍后重试')
    }
  }
  catch {
    coiMsgError('重置密码失败，请检查网络连接')
  }
}

// 取消重置密码
function handleCancelResetPassword() {
  resetPwdDialogRef.value?.coiClose()
  resetPwdForm.value = {
    newPassword: '',
    confirmPassword: '',
  }
}

// 查看头像
function handleViewAvatar(user: UserVo) {
  currentAvatarUser.value = user
  // 如果用户有头像URL，使用头像URL，否则生成默认头像
  if (user.avatar) {
    currentAvatar.value = user.avatar
  }
  else {
    // 为没有头像的用户生成一个SVG默认头像
    const firstLetter = user.loginName.charAt(0).toUpperCase()
    const colors = [
      'from-blue-400 to-purple-500',
      'from-green-400 to-blue-500',
      'from-pink-400 to-red-500',
      'from-yellow-400 to-orange-500',
      'from-indigo-400 to-purple-500',
    ]
    const colorIndex = firstLetter.charCodeAt(0) % colors.length
    const _gradientClass = colors[colorIndex]

    // 创建SVG默认头像
    const svg = `
      <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
            <stop offset="0%" style="stop-color:#3B82F6;stop-opacity:1" />
            <stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
          </linearGradient>
        </defs>
        <circle cx="100" cy="100" r="100" fill="url(#grad)" />
        <text x="100" y="120" font-family="Arial, sans-serif" font-size="80" font-weight="bold"
              text-anchor="middle" fill="white">${firstLetter}</text>
      </svg>
    `
    const blob = new Blob([svg], { type: 'image/svg+xml' })
    const blobUrl = URL.createObjectURL(blob)
    currentAvatar.value = blobUrl
    createdBlobUrls.value.push(blobUrl)
  }
  avatarDialogRef.value?.coiOpen()
}

// 关闭头像查看
function handleCloseAvatar() {
  avatarDialogRef.value?.coiClose()
  // 如果是生成的默认头像，释放URL
  if (currentAvatar.value.startsWith('blob:')) {
    URL.revokeObjectURL(currentAvatar.value)
    // 从追踪数组中移除
    const index = createdBlobUrls.value.indexOf(currentAvatar.value)
    if (index > -1) {
      createdBlobUrls.value.splice(index, 1)
    }
  }
  currentAvatar.value = ''
  currentAvatarUser.value = null
}

// 头像加载错误处理
function handleAvatarError(event: Event) {
  const img = event.target as HTMLImageElement
  if (currentAvatarUser.value) {
    // 重新生成默认头像
    const firstLetter = currentAvatarUser.value.loginName.charAt(0).toUpperCase()
    const svg = `
      <svg width="320" height="320" viewBox="0 0 320 320" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <linearGradient id="errorGrad" x1="0%" y1="0%" x2="100%" y2="100%">
            <stop offset="0%" style="stop-color:#EF4444;stop-opacity:1" />
            <stop offset="100%" style="stop-color:#F97316;stop-opacity:1" />
          </linearGradient>
        </defs>
        <rect width="320" height="320" rx="12" fill="url(#errorGrad)" />
        <text x="160" y="190" font-family="Arial, sans-serif" font-size="120" font-weight="bold"
              text-anchor="middle" fill="white">${firstLetter}</text>
      </svg>
    `
    const blob = new Blob([svg], { type: 'image/svg+xml' })
    const blobUrl = URL.createObjectURL(blob)
    img.src = blobUrl
    createdBlobUrls.value.push(blobUrl)
  }
}

// 导出功能
const showExportDropdown = ref(false)

// 下载文件的通用函数
function downloadBlob(blob: Blob, filename: string) {
  const url = URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = filename
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
  URL.revokeObjectURL(url)
}

// 生成文件名
function generateFileName(prefix: string) {
  const now = new Date()
  const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '')
  const timeStr = now.toTimeString().slice(0, 8).replace(/:/g, '')
  return `${prefix}_${dateStr}_${timeStr}.xlsx`
}

// 导出当前查询条件下的数据
async function handleExportCurrent() {
  try {
    showExportDropdown.value = false

    // 构建查询参数
    const { timeRange, ...otherParams } = searchForm.value
    const params: any = { ...otherParams }

    // 处理时间范围
    if (timeRange && timeRange.length === 2) {
      params.beginTime = new Date(timeRange[0]).toISOString().slice(0, 10)
      params.endTime = new Date(timeRange[1]).toISOString().slice(0, 10)
    }

    const response = await exportExcelData(params)
    const filename = generateFileName('用户数据_筛选')
    downloadBlob(response, filename)
    coiMsgSuccess('导出成功')
  }
  catch {
    coiMsgError('导出失败，请重试')
  }
}

// 导出全部数据
async function handleExportAll() {
  try {
    showExportDropdown.value = false

    const response = await exportExcelData()
    const filename = generateFileName('用户数据_全部')
    downloadBlob(response, filename)
    coiMsgSuccess('导出成功')
  }
  catch {
    coiMsgError('导出失败，请重试')
  }
}

// 下载导入模板
async function handleDownloadTemplate() {
  try {
    showExportDropdown.value = false

    const response = await downloadExcelTemplate()
    const filename = '用户导入模板.xlsx'
    downloadBlob(response, filename)
    coiMsgSuccess('模板下载成功')
  }
  catch {
    coiMsgError('模板下载失败，请重试')
  }
}

// 导出菜单处理
function handleExportMenuSelect(key: string) {
  switch (key) {
    case 'current':
      handleExportCurrent()
      break
    case 'all':
      handleExportAll()
      break
    case 'template':
      handleDownloadTemplate()
      break
  }
}

// 导入功能
function handleImport() {
  // 重置导入状态
  selectedFile.value = null
  updateSupport.value = false
  uploadProgress.value = 0
  importDialogRef.value?.coiOpen()
}

// 文件选择处理
function handleFileSelect(file: File) {
  // 添加调试日志

  // 验证文件类型
  const allowedTypes = [
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
    'application/vnd.ms-excel', // .xls
  ]

  if (!allowedTypes.includes(file.type)) {
    coiMsgError('请选择Excel文件（.xlsx或.xls格式）')
    return false
  }

  // 验证文件大小（限制为2MB）
  const fileSizeMB = file.size / 1024 / 1024
  if (fileSizeMB > 2) {
    coiMsgError(`Excel文件大小超出限制！当前文件：${fileSizeMB.toFixed(2)}MB，最大允许：2MB`)
    return false
  }

  selectedFile.value = file
  return true
}

// 执行导入
async function handleConfirmImport() {
  if (!selectedFile.value) {
    coiMsgError('请先选择要导入的文件')
    return
  }

  try {
    importLoading.value = true
    uploadProgress.value = 0

    // 清理之前的定时器
    if (progressInterval.value) {
      clearInterval(progressInterval.value)
      progressInterval.value = null
    }

    // 模拟上传进度
    progressInterval.value = setInterval(() => {
      if (uploadProgress.value < 90) {
        uploadProgress.value += 10
      }
    }, 200)

    const response = await importUserData(selectedFile.value, updateSupport.value)

    if (progressInterval.value) {
      clearInterval(progressInterval.value)
      progressInterval.value = null
    }
    uploadProgress.value = 100

    if (response.isSuccess && response.data) {
      const result = response.data
      coiMsgSuccess(
        `导入完成！总数：${result.total}，成功：${result.success}，失败：${result.failed}`,
      )

      // 关闭模态框并刷新列表
      importDialogRef.value?.coiClose()
      await getUserList()
    }
    else {
      coiMsgError(response.message || '导入失败，请重试')
    }
  }
  catch (error: any) {
    // 解析后端返回的错误信息
    let errorMessage = '导入失败，请检查文件格式或联系管理员'

    // 检查是否是服务返回的结构化错误
    if (error?.isSuccess === false) {
      // alova 返回的结构化错误
      if (error.message) {
        errorMessage = error.message
      }
      else if (error.msg) {
        errorMessage = error.msg
      }
    }
    else if (error?.response?.data?.msg) {
      // 原始HTTP响应错误
      errorMessage = error.response.data.msg
    }
    else if (error?.message) {
      // 网络错误或其他错误
      if (error.message.includes('413')) {
        errorMessage = 'Excel文件大小超出限制，请选择较小的文件'
      }
      else if (error.message.includes('400')) {
        errorMessage = 'Excel文件格式不支持或文件内容有误'
      }
      else {
        errorMessage = error.message
      }
    }

    coiMsgError(errorMessage)
  }
  finally {
    // 清理定时器
    if (progressInterval.value) {
      clearInterval(progressInterval.value)
      progressInterval.value = null
    }

    importLoading.value = false
    uploadProgress.value = 0
  }
}

// 取消导入
function handleCancelImport() {
  importDialogRef.value?.coiClose()
  selectedFile.value = null
  updateSupport.value = false
  uploadProgress.value = 0
}

// 分配角色
async function handleAssignRole(user: UserVo) {
  if (!user?.userId) {
    coiMsgError('用户信息无效')
    return
  }

  try {
    roleLoading.value = true
    currentAssignUser.value = user
    roleModalTitle.value = `分配角色 - ${user.userName}`

    // 重置状态
    availableRoles.value = []
    selectedRoleIds.value = []

    // 获取可用角色列表
    const response = await fetchNormalRoleForUser(user.userId)

    if (response.isSuccess && response.data) {
      const allRoles = response.data.data1 || []
      const userRoleIds = response.data.data2 || []

      if (Array.isArray(allRoles) && allRoles.length > 0) {
        // 直接使用后端返回的 label 和 value 字段
        availableRoles.value = allRoles.map(item => ({
          label: item.label,
          value: item.value,
        }))

        // 预选用户当前角色（使用后端返回的用户角色ID）
        selectedRoleIds.value = userRoleIds
        roleDialogRef.value?.coiOpen()
      }
      else {
        coiMsgInfo('当前系统没有可分配的角色')
      }
    }
    else {
      coiMsgError(response.msg || '获取角色列表失败')
    }
  }
  catch {
    coiMsgError('获取角色列表失败，请检查网络连接')
  }
  finally {
    roleLoading.value = false
  }
}

// 确认分配角色
async function handleConfirmAssignRole() {
  if (!currentAssignUser.value) {
    coiMsgError('用户信息缺失')
    return
  }

  try {
    roleLoading.value = true

    // 构建角色ID字符串
    // 如果没有选择角色，传递"-1"表示清空所有角色
    const roleIdsStr = selectedRoleIds.value.length > 0
      ? selectedRoleIds.value.join(',')
      : '-1'

    const response = await assignUserRole(currentAssignUser.value.userId, roleIdsStr)

    if (response.isSuccess) {
      coiMsgSuccess('角色分配成功')
      roleDialogRef.value?.coiClose()

      // 更新当前用户的角色信息
      if (currentAssignUser.value) {
        currentAssignUser.value.roleIds = selectedRoleIds.value
      }

      // 刷新用户列表
      await getUserList()
    }
    else {
      coiMsgError(response.msg || '角色分配失败')
    }
  }
  catch {
    coiMsgError('角色分配失败，请检查网络连接')
  }
  finally {
    roleLoading.value = false
  }
}

// 取消分配角色
function handleCancelAssignRole() {
  roleDialogRef.value?.coiClose()
  currentAssignUser.value = null
  selectedRoleIds.value = []
  availableRoles.value = []
  roleModalTitle.value = '分配角色'
  roleLoading.value = false
}

// 提交表单
async function handleSubmit() {
  if (!formRef.value)
    return

  try {
    // 先进行表单验证
    await formRef.value.validate()
  }
  catch {
    // 表单验证失败，提示用户检查填写内容
    coiMsgWarning('验证失败，请检查填写内容')
    return
  }

  // 表单验证通过，执行API调用
  try {
    const submitData = {
      ...formData.value,
    }

    delete submitData.confirmPassword

    if (isEdit.value && !submitData.password) {
      delete submitData.password
    }

    if (isEdit.value && currentUser.value) {
      submitData.userId = currentUser.value.userId
    }

    const { isSuccess } = isEdit.value ? await updateUser(submitData) : await addUser(submitData)

    if (isSuccess) {
      coiMsgSuccess(isEdit.value ? '用户信息更新成功' : '用户创建成功')
      userDialogRef.value?.coiClose()
      await getUserList()
    }
    else {
      coiMsgError(isEdit.value ? '更新失败，请稍后重试' : '创建失败，请稍后重试')
    }
  }
  catch {
    coiMsgError(isEdit.value ? '更新失败，请检查网络连接' : '创建失败，请检查网络连接')
  }
}

// 取消操作
function handleCancel() {
  userDialogRef.value?.coiClose()
}

// 判断是否有搜索条件
function hasSearchConditions() {
  return Object.values(searchForm.value).some((value) => {
    if (value === null || value === undefined || value === '') {
      return false
    }
    if (Array.isArray(value) && value.length === 0) {
      return false
    }
    return true
  })
}

// 获取空状态类型
function getEmptyType() {
  if (hasSearchConditions()) {
    return 'search'
  }
  return 'default'
}

// 获取空状态标题
function getEmptyTitle() {
  if (hasSearchConditions()) {
    return '搜索无结果'
  }
  return '暂无用户数据'
}

// 获取空状态描述
function getEmptyDescription() {
  if (hasSearchConditions()) {
    return '未找到符合搜索条件的用户，请尝试调整搜索条件或重置筛选'
  }
  return '当前还没有用户数据，点击"新增"按钮创建第一个用户'
}

// 获取空状态操作文字
function getEmptyActionText() {
  if (hasSearchConditions()) {
    return '重置筛选'
  }
  return '新增用户'
}

// 处理空状态操作
function handleEmptyAction() {
  if (hasSearchConditions()) {
    handleReset()
  }
  else {
    handleAdd()
  }
}

// 自定义角色标签渲染
function renderRoleTag({ option, handleClose }: { option: any, handleClose: () => void }) {
  return h(NTag, {
    type: 'primary',
    size: 'small',
    closable: true,
    onClose: handleClose,
    style: {
      marginRight: '6px',
      marginBottom: '2px',
      borderRadius: '6px',
      fontSize: '12px',
      padding: '4px 8px',
      background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
      border: 'none',
      color: '#ffffff',
      boxShadow: '0 2px 4px rgba(99, 102, 241, 0.2)',
    },
  }, {
    default: () => option.label,
  })
}

// 组件挂载时获取数据
onMounted(() => {
  getUserList()
  getRoleList()
})

// 组件卸载前清理资源
onBeforeUnmount(() => {
  // 清理定时器
  if (progressInterval.value) {
    clearInterval(progressInterval.value)
    progressInterval.value = null
  }

  // 清理所有创建的Blob URL
  createdBlobUrls.value.forEach((url) => {
    URL.revokeObjectURL(url)
  })
  createdBlobUrls.value = []

  // 清理当前头像URL
  if (currentAvatar.value.startsWith('blob:')) {
    URL.revokeObjectURL(currentAvatar.value)
  }
})
</script>

<style scoped>
.user-management {
  height: 100vh;
  overflow: hidden;
}

.custom-table :deep(.n-data-table-td) {
  padding: 8px 12px;
}

.custom-table :deep(.n-data-table-th) {
  padding: 10px 12px;
  background-color: #fafafa;
  font-weight: normal;
  color: #262626;
}

.custom-table :deep(.n-data-table-tr:hover .n-data-table-td) {
  background-color: #f8faff;
}

.search-form :deep(.n-form-item-label) {
  font-weight: 500;
  color: #262626;
}

.user-modal :deep(.n-card-header) {
  padding: 24px 24px 0;
  font-size: 18px;
  font-weight: 600;
}

.user-modal :deep(.n-card__content) {
  padding: 24px;
}

.role-assign-modal :deep(.n-card-header) {
  padding: 20px 24px 0;
  font-size: 18px;
  font-weight: 600;
}

.role-assign-modal :deep(.n-card__content) {
  padding: 20px 24px;
}

.role-transfer :deep(.n-transfer-list) {
  height: 300px;
}

.role-transfer :deep(.n-transfer-list-header) {
  font-weight: 600;
  background-color: #f5f5f5;
}

.avatar-modal :deep(.n-card-header) {
  padding: 20px 24px 0;
  border-bottom: 1px solid #f0f0f0;
}

.avatar-modal :deep(.n-card__content) {
  padding: 24px;
}

.avatar-modal :deep(.n-card__footer) {
  padding: 0 24px 20px;
  border-top: 1px solid #f0f0f0;
}

.import-modal :deep(.n-card-header) {
  padding: 16px 20px 0;
  border-bottom: 1px solid #f0f0f0;
}

.import-modal :deep(.n-card__content) {
  padding: 20px;
}

.import-modal :deep(.n-card__footer) {
  padding: 12px 20px;
  border-top: 1px solid #f0f0f0;
}

.import-modal :deep(.n-upload-dragger) {
  border: 2px dashed #d9d9d9;
  border-radius: 6px;
  background: #fafafa;
  padding: 40px 20px;
  transition: border-color 0.3s;
}

.import-modal :deep(.n-upload-dragger:hover) {
  border-color: #40a9ff;
}

/* CoiEmpty按钮样式 */
.coi-empty__action-btn {
  position: relative;
  overflow: hidden;
  transition: all 0.3s ease;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
  border: none;
}

.coi-empty__action-btn::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
  transition: left 0.6s ease;
}

.coi-empty__action-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}

.coi-empty__action-btn:hover::before {
  left: 100%;
}

/* 操作按钮样式 */
.action-btn {
  width: 36px !important;
  height: 36px !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  border-radius: 50% !important;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08) !important;
  border: none !important;
  position: relative !important;
  overflow: hidden !important;
}

.action-btn:hover {
  transform: translateY(-2px) !important;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15) !important;
}

.action-btn:active {
  transform: translateY(0) !important;
  transition: all 0.1s !important;
}

/* 编辑按钮 */
.action-btn-edit {
  background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%) !important;
  color: white !important;
}

.action-btn-edit:hover {
  background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%) !important;
  box-shadow: 0 4px 16px rgba(59, 130, 246, 0.4) !important;
}

/* 删除按钮 */
.action-btn-delete {
  background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%) !important;
  color: white !important;
}

.action-btn-delete:hover {
  background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%) !important;
  box-shadow: 0 4px 16px rgba(239, 68, 68, 0.4) !important;
}

/* 警告按钮 */
.action-btn-warning {
  background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%) !important;
  color: white !important;
}

.action-btn-warning:hover {
  background: linear-gradient(135deg, #d97706 0%, #b45309 100%) !important;
  box-shadow: 0 4px 16px rgba(245, 158, 11, 0.4) !important;
}

/* 信息按钮 */
.action-btn-info {
  background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%) !important;
  color: white !important;
}

.action-btn-info:hover {
  background: linear-gradient(135deg, #0891b2 0%, #0e7490 100%) !important;
  box-shadow: 0 4px 16px rgba(6, 182, 212, 0.4) !important;
}

/* 图标居中对齐 */
.action-btn .n-icon {
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  margin: 0 !important;
}

/* 移除默认边框和背景 */
.action-btn.n-button {
  border: none !important;
}

.action-btn.n-button:focus {
  outline: none !important;
  box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2) !important;
}

/* 邮箱字段样式 - 确保tooltip文字清晰 */
.email-cell-text {
  color: #6b7280; /* 对应 text-gray-500，在表格中显示为灰色 */
}

/* 确保邮箱字段的tooltip内文字清晰可见，覆盖继承的灰色 */
:deep(.n-tooltip) .email-cell-text,
:deep(.n-tooltip__content) .email-cell-text,
:deep(.n-ellipsis__tooltip) .email-cell-text {
  color: #ffffff !important; /* 强制tooltip内文字为白色 */
}

/* 针对ellipsis tooltip的特殊处理 */
:deep(.n-ellipsis__tooltip) {
  color: #ffffff !important;
}

:deep(.n-ellipsis__tooltip *) {
  color: #ffffff !important;
}

/* 角色选择器样式优化 */
.role-select :deep(.n-base-selection) {
  border-radius: 8px;
  border: 1px solid #e5e7eb;
  transition: all 0.3s ease;
}

.role-select :deep(.n-base-selection:hover) {
  border-color: #6366f1;
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}

.role-select :deep(.n-base-selection.n-base-selection--focus) {
  border-color: #6366f1;
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
}

.role-select :deep(.n-base-selection-tags) {
  padding: 4px;
  min-height: 40px;
}

/* 角色标签样式 */
.role-select :deep(.n-tag) {
  background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%) !important;
  border: none !important;
  color: #ffffff !important;
  border-radius: 6px !important;
  font-size: 12px !important;
  font-weight: 500 !important;
  padding: 4px 8px !important;
  margin: 2px 4px 2px 0 !important;
  box-shadow: 0 2px 4px rgba(99, 102, 241, 0.2) !important;
  transition: all 0.2s ease !important;
}

.role-select :deep(.n-tag:hover) {
  transform: translateY(-1px) !important;
  box-shadow: 0 4px 8px rgba(99, 102, 241, 0.3) !important;
}

.role-select :deep(.n-tag .n-base-close) {
  color: rgba(255, 255, 255, 0.8) !important;
  transition: color 0.2s ease !important;
}

.role-select :deep(.n-tag .n-base-close:hover) {
  color: #ffffff !important;
  background: rgba(255, 255, 255, 0.2) !important;
  border-radius: 50% !important;
}
</style>
