2025-07-30 09:13:52 +08:00
|
|
|
|
<template>
|
|
|
|
|
<GiPageLayout>
|
|
|
|
|
<template #left>
|
|
|
|
|
<DeptTree @node-click="handleSelectDept" />
|
|
|
|
|
</template>
|
|
|
|
|
<GiTable
|
|
|
|
|
row-key="userId"
|
|
|
|
|
:data="dataList"
|
|
|
|
|
:columns="columns"
|
|
|
|
|
:loading="loading"
|
|
|
|
|
:scroll="{ x: '100%', y: '100%', minWidth: 1500 }"
|
|
|
|
|
:pagination="pagination"
|
|
|
|
|
:disabled-tools="['size']"
|
|
|
|
|
:disabled-column-keys="['name']"
|
|
|
|
|
@page-change="handlePageChange"
|
|
|
|
|
@page-size-change="handlePageSizeChange"
|
|
|
|
|
@refresh="search"
|
|
|
|
|
>
|
|
|
|
|
<template #top>
|
|
|
|
|
<GiForm v-model="queryForm" search :columns="queryFormColumns" size="medium" @search="search" @reset="reset"></GiForm>
|
|
|
|
|
</template>
|
|
|
|
|
<template #toolbar-left>
|
|
|
|
|
<a-button v-permission="['system:user:create']" type="primary" @click="onAdd">
|
|
|
|
|
<template #icon><icon-plus /></template>
|
|
|
|
|
<template #default>新增</template>
|
|
|
|
|
</a-button>
|
|
|
|
|
<a-button v-permission="['system:user:import']" @click="onImport">
|
|
|
|
|
<template #icon><icon-upload /></template>
|
|
|
|
|
<template #default>导入</template>
|
|
|
|
|
</a-button>
|
|
|
|
|
</template>
|
|
|
|
|
<template #toolbar-right>
|
|
|
|
|
<a-button v-permission="['system:user:export']" @click="onExport">
|
|
|
|
|
<template #icon><icon-download /></template>
|
|
|
|
|
<template #default>导出</template>
|
|
|
|
|
</a-button>
|
|
|
|
|
</template>
|
|
|
|
|
<template #name="{ record }">
|
|
|
|
|
<GiCellAvatar :avatar="record.avatar" :name="record.name" />
|
|
|
|
|
</template>
|
|
|
|
|
<template #gender="{ record }">
|
|
|
|
|
<GiCellGender :gender="record.gender" />
|
|
|
|
|
</template>
|
|
|
|
|
<template #roleIds="{ record }">
|
|
|
|
|
<GiCellTags :data="record.roleIds" />
|
|
|
|
|
</template>
|
|
|
|
|
<template #status="{ record }">
|
|
|
|
|
<GiCellStatus :status="record.status" />
|
|
|
|
|
</template>
|
|
|
|
|
<template #userStatus="{ record }">
|
|
|
|
|
<a-tag :color="record.userStatus === 'ENABLED' ? 'green' : 'red'" size="small">
|
|
|
|
|
{{ record.userStatusLabel || record.userStatus }}
|
|
|
|
|
</a-tag>
|
|
|
|
|
</template>
|
|
|
|
|
<template #userType="{ record }">
|
|
|
|
|
<a-tag color="blue" size="small">
|
|
|
|
|
{{ record.userTypeLabel || record.userType }}
|
|
|
|
|
</a-tag>
|
|
|
|
|
</template>
|
|
|
|
|
<template #action="{ record }">
|
|
|
|
|
<a-space>
|
|
|
|
|
<a-link v-permission="['system:user:get']" title="详情" @click="onDetail(record)">详情</a-link>
|
|
|
|
|
<a-link v-permission="['system:user:update']" title="修改" @click="onUpdate(record)">修改</a-link>
|
|
|
|
|
<a-link
|
|
|
|
|
v-permission="['system:user:delete']"
|
|
|
|
|
status="danger"
|
2025-07-31 17:32:21 +08:00
|
|
|
|
title="删除"
|
2025-07-30 09:13:52 +08:00
|
|
|
|
@click="onDelete(record)"
|
|
|
|
|
>
|
|
|
|
|
删除
|
|
|
|
|
</a-link>
|
|
|
|
|
<a-dropdown>
|
|
|
|
|
<a-button v-if="has.hasPermOr(['system:user:resetPwd', 'system:user:updateRole'])" type="text" size="mini" title="更多">
|
|
|
|
|
<template #icon>
|
|
|
|
|
<icon-more :size="16" />
|
|
|
|
|
</template>
|
|
|
|
|
</a-button>
|
|
|
|
|
<template #content>
|
|
|
|
|
<a-doption v-permission="['system:user:resetPwd']" title="重置密码" @click="onResetPwd(record)">重置密码</a-doption>
|
|
|
|
|
<a-doption v-permission="['system:user:updateRole']" title="分配角色" @click="onUpdateRole(record)">分配角色</a-doption>
|
|
|
|
|
</template>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
</a-space>
|
|
|
|
|
</template>
|
|
|
|
|
</GiTable>
|
|
|
|
|
|
|
|
|
|
<UserAddDrawer ref="UserAddDrawerRef" @save-success="search" />
|
|
|
|
|
<UserImportDrawer ref="UserImportDrawerRef" @save-success="search" />
|
|
|
|
|
<UserDetailDrawer ref="UserDetailDrawerRef" />
|
|
|
|
|
<UserResetPwdModal ref="UserResetPwdModalRef" />
|
|
|
|
|
<UserUpdateRoleModal ref="UserUpdateRoleModalRef" @save-success="search" />
|
|
|
|
|
</GiPageLayout>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import type { TableInstance } from '@arco-design/web-vue'
|
2025-07-31 17:32:21 +08:00
|
|
|
|
import { Message, Modal } from '@arco-design/web-vue'
|
|
|
|
|
import { onMounted } from 'vue'
|
2025-07-30 09:13:52 +08:00
|
|
|
|
import DeptTree from './dept/index.vue'
|
|
|
|
|
import UserAddDrawer from './UserAddDrawer.vue'
|
|
|
|
|
import UserImportDrawer from './UserImportDrawer.vue'
|
|
|
|
|
import UserDetailDrawer from './UserDetailDrawer.vue'
|
|
|
|
|
import UserResetPwdModal from './UserResetPwdModal.vue'
|
|
|
|
|
import UserUpdateRoleModal from './UserUpdateRoleModal.vue'
|
2025-07-31 17:32:21 +08:00
|
|
|
|
import {
|
|
|
|
|
deleteUserNew,
|
2025-07-30 09:13:52 +08:00
|
|
|
|
listUserNew,
|
|
|
|
|
} from '@/apis/system/user-new'
|
|
|
|
|
import { exportUser } from '@/apis/system/user'
|
2025-07-31 17:32:21 +08:00
|
|
|
|
import type { UserNewQuery, UserNewResp } from '@/apis/system/type'
|
|
|
|
|
import { useDownload, useResetReactive } from '@/hooks'
|
2025-07-30 09:13:52 +08:00
|
|
|
|
import { isMobile } from '@/utils'
|
|
|
|
|
import has from '@/utils/has'
|
|
|
|
|
import type { ColumnItem } from '@/components/GiForm'
|
|
|
|
|
|
|
|
|
|
defineOptions({ name: 'SystemUser' })
|
|
|
|
|
|
|
|
|
|
const [queryForm, resetForm] = useResetReactive({
|
|
|
|
|
account: undefined,
|
|
|
|
|
name: undefined,
|
|
|
|
|
userStatus: undefined,
|
|
|
|
|
})
|
|
|
|
|
const queryFormColumns: ColumnItem[] = reactive([
|
|
|
|
|
{
|
|
|
|
|
type: 'input',
|
|
|
|
|
label: '用户名',
|
|
|
|
|
field: 'account',
|
|
|
|
|
span: { xs: 24, sm: 8, xxl: 8 },
|
|
|
|
|
props: {
|
|
|
|
|
placeholder: '账号/姓名/手机号',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: 'input',
|
|
|
|
|
label: '姓名',
|
|
|
|
|
field: 'name',
|
|
|
|
|
span: { xs: 24, sm: 8, xxl: 8 },
|
|
|
|
|
props: {
|
|
|
|
|
placeholder: '请输入姓名',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: 'select',
|
|
|
|
|
label: '在职状态',
|
|
|
|
|
field: 'userStatus',
|
|
|
|
|
span: { xs: 24, sm: 8, xxl: 8 },
|
|
|
|
|
props: {
|
|
|
|
|
options: [
|
|
|
|
|
{ label: '在职', value: 'ENABLED' },
|
2025-07-31 17:32:21 +08:00
|
|
|
|
{ label: '离职', value: 'DISABLED' },
|
2025-07-30 09:13:52 +08:00
|
|
|
|
],
|
|
|
|
|
placeholder: '请选择在职状态',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
// 保存所有用户数据和筛选后的用户数据
|
|
|
|
|
const allUsers = ref<UserNewResp[]>([])
|
|
|
|
|
const filteredUsers = ref<UserNewResp[]>([])
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
// 当前选中的部门名称
|
|
|
|
|
const selectedDeptName = ref<string>('')
|
|
|
|
|
|
|
|
|
|
// 分页配置
|
|
|
|
|
const pagination = reactive({
|
|
|
|
|
current: 1,
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
total: 0,
|
|
|
|
|
showTotal: true,
|
|
|
|
|
showJumper: true,
|
|
|
|
|
showPageSize: true,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 获取所有用户数据
|
|
|
|
|
const fetchAllUsers = async () => {
|
|
|
|
|
try {
|
|
|
|
|
loading.value = true
|
|
|
|
|
// 直接使用 /user/list 接口获取所有用户
|
|
|
|
|
const { data } = await listUserNew({
|
|
|
|
|
account: queryForm.account,
|
|
|
|
|
name: queryForm.name,
|
|
|
|
|
userStatus: queryForm.userStatus,
|
2025-07-31 17:32:21 +08:00
|
|
|
|
sort: undefined,
|
2025-07-30 09:13:52 +08:00
|
|
|
|
} as UserNewQuery)
|
|
|
|
|
console.log(data)
|
|
|
|
|
allUsers.value = data || []
|
2025-07-31 17:32:21 +08:00
|
|
|
|
|
2025-07-30 09:13:52 +08:00
|
|
|
|
// 根据当前筛选条件过滤用户
|
|
|
|
|
filterUsersByDept()
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取用户列表失败:', error)
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据部门名称筛选用户
|
|
|
|
|
const filterUsersByDept = () => {
|
|
|
|
|
if (!selectedDeptName.value) {
|
|
|
|
|
// 没有选择部门,显示所有用户
|
|
|
|
|
filteredUsers.value = [...allUsers.value]
|
|
|
|
|
} else {
|
|
|
|
|
// 有部门名称,筛选该部门的用户
|
2025-07-31 17:32:21 +08:00
|
|
|
|
filteredUsers.value = allUsers.value.filter((user) => user.deptName === selectedDeptName.value)
|
2025-07-30 09:13:52 +08:00
|
|
|
|
}
|
2025-07-31 17:32:21 +08:00
|
|
|
|
|
2025-07-30 09:13:52 +08:00
|
|
|
|
// 更新分页总数
|
|
|
|
|
pagination.total = filteredUsers.value.length
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 当前页数据
|
|
|
|
|
const dataList = computed(() => {
|
|
|
|
|
const start = (pagination.current - 1) * pagination.pageSize
|
|
|
|
|
const end = start + pagination.pageSize
|
|
|
|
|
return filteredUsers.value.slice(start, end)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 处理分页变化
|
|
|
|
|
const handlePageChange = (current: number) => {
|
|
|
|
|
pagination.current = current
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理每页条数变化
|
|
|
|
|
const handlePageSizeChange = (pageSize: number) => {
|
|
|
|
|
pagination.pageSize = pageSize
|
|
|
|
|
pagination.current = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 重置
|
|
|
|
|
const reset = () => {
|
|
|
|
|
resetForm()
|
|
|
|
|
fetchAllUsers()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 搜索
|
|
|
|
|
const search = () => {
|
|
|
|
|
pagination.current = 1
|
|
|
|
|
fetchAllUsers()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 删除
|
2025-07-31 17:32:21 +08:00
|
|
|
|
const handleDelete = (callback: () => Promise<any>, options: { content?: string, showModal?: boolean } = {}) => {
|
2025-07-30 09:13:52 +08:00
|
|
|
|
const { content, showModal = false } = options
|
|
|
|
|
const doDelete = async () => {
|
|
|
|
|
loading.value = true
|
|
|
|
|
try {
|
|
|
|
|
await callback()
|
|
|
|
|
// 删除成功后重新获取数据
|
|
|
|
|
fetchAllUsers()
|
|
|
|
|
Message.success('删除成功')
|
|
|
|
|
return true
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('删除失败:', error)
|
|
|
|
|
Message.error('删除失败')
|
|
|
|
|
return false
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (showModal) {
|
|
|
|
|
Modal.confirm({
|
|
|
|
|
title: '确认删除',
|
|
|
|
|
content: content || '确定要删除此记录吗?',
|
|
|
|
|
okText: '确认',
|
|
|
|
|
cancelText: '取消',
|
2025-07-31 17:32:21 +08:00
|
|
|
|
onOk: () => doDelete(),
|
2025-07-30 09:13:52 +08:00
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return doDelete()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 删除用户
|
|
|
|
|
const onDelete = (record: UserNewResp) => {
|
|
|
|
|
if (!record || !record.userId) {
|
|
|
|
|
return Message.warning('用户ID不存在,无法删除')
|
|
|
|
|
}
|
2025-07-31 17:32:21 +08:00
|
|
|
|
|
2025-07-30 09:13:52 +08:00
|
|
|
|
return handleDelete(
|
2025-07-31 17:32:21 +08:00
|
|
|
|
() => deleteUserNew(record.userId),
|
2025-07-30 09:13:52 +08:00
|
|
|
|
{
|
|
|
|
|
content: `是否确定删除用户「${record.name || ''}(${record.account || ''})」?`,
|
2025-07-31 17:32:21 +08:00
|
|
|
|
showModal: true,
|
|
|
|
|
},
|
2025-07-30 09:13:52 +08:00
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 导出
|
|
|
|
|
const onExport = () => {
|
2025-07-31 17:32:21 +08:00
|
|
|
|
useDownload(() => exportUser({ ...queryForm, sort: undefined }))
|
2025-07-30 09:13:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const columns: TableInstance['columns'] = [
|
|
|
|
|
{
|
|
|
|
|
title: '序号',
|
|
|
|
|
width: 66,
|
|
|
|
|
align: 'center',
|
|
|
|
|
render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize),
|
|
|
|
|
fixed: !isMobile() ? 'left' : undefined,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '姓名',
|
|
|
|
|
dataIndex: 'name',
|
|
|
|
|
slotName: 'name',
|
|
|
|
|
minWidth: 140,
|
|
|
|
|
ellipsis: true,
|
|
|
|
|
tooltip: true,
|
|
|
|
|
fixed: !isMobile() ? 'left' : undefined,
|
|
|
|
|
},
|
|
|
|
|
{ title: '账号', dataIndex: 'account', minWidth: 140, ellipsis: true, tooltip: true },
|
|
|
|
|
{ title: '员工编码', dataIndex: 'userCode', minWidth: 120, ellipsis: true, tooltip: true },
|
|
|
|
|
{ title: '在职状态', dataIndex: 'userStatus', slotName: 'userStatus', align: 'center', width: 100 },
|
|
|
|
|
{ title: '员工性质', dataIndex: 'userType', slotName: 'userType', align: 'center', width: 100 },
|
|
|
|
|
{ title: '性别', dataIndex: 'gender', slotName: 'gender', align: 'center', width: 80 },
|
|
|
|
|
{ title: '所属部门', dataIndex: 'deptName', minWidth: 180, ellipsis: true, tooltip: true },
|
|
|
|
|
{ title: '角色', dataIndex: 'roleIds', slotName: 'roleIds', minWidth: 165 },
|
|
|
|
|
{ title: '手机号', dataIndex: 'mobile', minWidth: 170, ellipsis: true, tooltip: true },
|
|
|
|
|
{ title: '邮箱', dataIndex: 'email', minWidth: 170, ellipsis: true, tooltip: true },
|
|
|
|
|
{ title: '入职日期', dataIndex: 'hiredate', width: 120, ellipsis: true, tooltip: true },
|
|
|
|
|
{ title: '出生日期', dataIndex: 'birthdate', width: 120, ellipsis: true, tooltip: true, show: false },
|
|
|
|
|
{ title: '学历', dataIndex: 'educationLabel', width: 100, ellipsis: true, tooltip: true, show: false },
|
|
|
|
|
{ title: '专业', dataIndex: 'majorField', width: 120, ellipsis: true, tooltip: true, show: false },
|
|
|
|
|
{ title: '工作方向', dataIndex: 'workField', width: 120, ellipsis: true, tooltip: true, show: false },
|
|
|
|
|
{ title: '身份证', dataIndex: 'identityCard', width: 180, ellipsis: true, tooltip: true, show: false },
|
|
|
|
|
{
|
|
|
|
|
title: '操作',
|
|
|
|
|
dataIndex: 'action',
|
|
|
|
|
slotName: 'action',
|
|
|
|
|
width: 190,
|
|
|
|
|
align: 'center',
|
|
|
|
|
fixed: !isMobile() ? 'right' : undefined,
|
|
|
|
|
show: has.hasPermOr([
|
|
|
|
|
'system:user:get',
|
|
|
|
|
'system:user:update',
|
|
|
|
|
'system:user:delete',
|
|
|
|
|
'system:user:resetPwd',
|
|
|
|
|
'system:user:updateRole',
|
|
|
|
|
]),
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// 根据选中部门查询
|
|
|
|
|
const handleSelectDept = (keys: Array<any>, node?: any) => {
|
|
|
|
|
// 如果选择"all",则设置部门名称为空字符串,查询全部
|
|
|
|
|
if (keys.length === 1 && keys[0] === 'all') {
|
|
|
|
|
selectedDeptName.value = ''
|
|
|
|
|
} else if (node && node.title) {
|
|
|
|
|
// 设置选中的部门名称
|
|
|
|
|
selectedDeptName.value = node.title
|
|
|
|
|
} else {
|
|
|
|
|
selectedDeptName.value = ''
|
|
|
|
|
}
|
2025-07-31 17:32:21 +08:00
|
|
|
|
|
2025-07-30 09:13:52 +08:00
|
|
|
|
// 仅在本地过滤数据,不重新请求API
|
|
|
|
|
filterUsersByDept()
|
|
|
|
|
// 重置分页到第一页
|
|
|
|
|
pagination.current = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UserImportDrawerRef = ref<InstanceType<typeof UserImportDrawer>>()
|
|
|
|
|
// 导入
|
|
|
|
|
const onImport = () => {
|
|
|
|
|
UserImportDrawerRef.value?.onOpen()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UserAddDrawerRef = ref<InstanceType<typeof UserAddDrawer>>()
|
|
|
|
|
// 新增
|
|
|
|
|
const onAdd = () => {
|
|
|
|
|
UserAddDrawerRef.value?.onAdd()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 修改
|
|
|
|
|
const onUpdate = (record: UserNewResp) => {
|
|
|
|
|
UserAddDrawerRef.value?.onUpdate(record.userId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UserDetailDrawerRef = ref<InstanceType<typeof UserDetailDrawer>>()
|
|
|
|
|
// 详情
|
|
|
|
|
const onDetail = (record: UserNewResp) => {
|
|
|
|
|
UserDetailDrawerRef.value?.onOpen(record.userId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UserResetPwdModalRef = ref<InstanceType<typeof UserResetPwdModal>>()
|
|
|
|
|
// 重置密码
|
|
|
|
|
const onResetPwd = (record: UserNewResp) => {
|
|
|
|
|
UserResetPwdModalRef.value?.onOpen(record.userId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UserUpdateRoleModalRef = ref<InstanceType<typeof UserUpdateRoleModal>>()
|
|
|
|
|
// 分配角色
|
|
|
|
|
const onUpdateRole = (record: UserNewResp) => {
|
|
|
|
|
UserUpdateRoleModalRef.value?.onOpen(record.userId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 声明是否首次加载,首次加载时展示所有用户
|
|
|
|
|
const isFirstLoad = ref(true)
|
|
|
|
|
|
|
|
|
|
// 初始加载
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
// 初始加载所有用户数据
|
|
|
|
|
fetchAllUsers()
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.page_header {
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page_content {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: auto;
|
|
|
|
|
}
|
|
|
|
|
</style>
|