281 lines
9.5 KiB
Vue
281 lines
9.5 KiB
Vue
<template>
|
|
<div class="page-container">
|
|
<!-- 总览卡片 -->
|
|
<a-row :gutter="[16, 16]" class="stat-row">
|
|
<a-col :xs="12" :sm="6">
|
|
<div class="mini-stat-card">
|
|
<div class="stat-content">
|
|
<span class="stat-label">管理员总数</span>
|
|
<span class="stat-num">{{ stats.total }}</span>
|
|
</div>
|
|
<UserOutlined class="stat-bg-icon" />
|
|
</div>
|
|
</a-col>
|
|
<a-col :xs="12" :sm="6">
|
|
<div class="mini-stat-card">
|
|
<div class="stat-content">
|
|
<span class="stat-label">正常状态</span>
|
|
<span class="stat-num green">{{ stats.active }}</span>
|
|
</div>
|
|
<CheckCircleOutlined class="stat-bg-icon" />
|
|
</div>
|
|
</a-col>
|
|
<a-col :xs="12" :sm="6">
|
|
<div class="mini-stat-card">
|
|
<div class="stat-content">
|
|
<span class="stat-label">禁用状态</span>
|
|
<span class="stat-num red">{{ stats.disabled }}</span>
|
|
</div>
|
|
<StopOutlined class="stat-bg-icon" />
|
|
</div>
|
|
</a-col>
|
|
<a-col :xs="12" :sm="6">
|
|
<div class="mini-stat-card">
|
|
<div class="stat-content">
|
|
<span class="stat-label">角色数量</span>
|
|
<span class="stat-num blue">{{ allRoles.length }}</span>
|
|
</div>
|
|
<SafetyOutlined class="stat-bg-icon" />
|
|
</div>
|
|
</a-col>
|
|
</a-row>
|
|
|
|
<a-card :bordered="false">
|
|
<!-- 搜索栏 -->
|
|
<div class="filter-bar">
|
|
<div class="filter-left">
|
|
<span class="filter-result">共 <b>{{ pagination.total }}</b> 条记录</span>
|
|
</div>
|
|
<div class="filter-right">
|
|
<a-input
|
|
v-model:value="searchForm.keyword"
|
|
placeholder="搜索用户名/姓名"
|
|
allow-clear
|
|
style="width: 180px"
|
|
@input="handleSearch"
|
|
>
|
|
<template #prefix><SearchOutlined /></template>
|
|
</a-input>
|
|
<a-select
|
|
v-model:value="searchForm.status"
|
|
placeholder="状态"
|
|
allow-clear
|
|
style="width: 100px"
|
|
@change="handleSearch"
|
|
>
|
|
<a-select-option :value="1">正常</a-select-option>
|
|
<a-select-option :value="0">禁用</a-select-option>
|
|
</a-select>
|
|
<a-button @click="handleReset">重置</a-button>
|
|
<a-button type="primary" v-permission="'system:admin:add'" @click="handleAdd">
|
|
<PlusOutlined /> 新增管理员
|
|
</a-button>
|
|
</div>
|
|
</div>
|
|
|
|
<a-table
|
|
:columns="columns"
|
|
:data-source="tableData"
|
|
:loading="loading"
|
|
:pagination="pagination"
|
|
row-key="id"
|
|
:scroll="{ x: 1000 }"
|
|
@change="handleTableChange"
|
|
>
|
|
<template #bodyCell="{ column, record }">
|
|
<template v-if="column.key === 'roles'">
|
|
<a-tag v-for="role in record.roles" :key="role.id" color="blue" size="small">
|
|
{{ role.name }}
|
|
</a-tag>
|
|
<span v-if="!record.roles?.length" style="color: #999">-</span>
|
|
</template>
|
|
<template v-if="column.key === 'status'">
|
|
<a-badge :status="record.status === 1 ? 'success' : 'error'" :text="record.status === 1 ? '正常' : '禁用'" />
|
|
</template>
|
|
<template v-if="column.key === 'action'">
|
|
<a-space :size="0" split>
|
|
<a-button type="link" size="small" v-permission="'system:admin:edit'" @click="handleEdit(record)">编辑</a-button>
|
|
<a-popconfirm title="确定删除该管理员吗?" @confirm="handleDelete(record)">
|
|
<a-button type="link" size="small" danger v-permission="'system:admin:delete'">删除</a-button>
|
|
</a-popconfirm>
|
|
</a-space>
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
</a-card>
|
|
|
|
<!-- 新增/编辑弹窗 -->
|
|
<a-modal v-model:open="modalVisible" :title="isEdit ? '编辑管理员' : '新增管理员'" @ok="handleSubmit" :confirm-loading="submitting" width="560px">
|
|
<a-form :model="formData" :label-col="{ span: 5 }" style="margin-top: 16px">
|
|
<a-form-item label="用户名" required>
|
|
<a-input v-model:value="formData.username" :disabled="isEdit" placeholder="请输入用户名" />
|
|
</a-form-item>
|
|
<a-form-item v-if="!isEdit" label="密码" required>
|
|
<a-input-password v-model:value="formData.password" placeholder="请输入密码" />
|
|
</a-form-item>
|
|
<a-form-item v-if="isEdit" label="修改密码">
|
|
<a-input-password v-model:value="formData.password" placeholder="留空则不修改密码" />
|
|
</a-form-item>
|
|
<a-form-item label="真实姓名">
|
|
<a-input v-model:value="formData.realName" placeholder="请输入真实姓名" />
|
|
</a-form-item>
|
|
<a-form-item label="手机号">
|
|
<a-input v-model:value="formData.phone" placeholder="请输入手机号" />
|
|
</a-form-item>
|
|
<a-form-item label="邮箱">
|
|
<a-input v-model:value="formData.email" placeholder="请输入邮箱" />
|
|
</a-form-item>
|
|
<a-form-item label="角色" required>
|
|
<a-select v-model:value="formData.roleIds" mode="multiple" placeholder="请选择角色">
|
|
<a-select-option v-for="role in allRoles" :key="role.id" :value="role.id">{{ role.name }}</a-select-option>
|
|
</a-select>
|
|
</a-form-item>
|
|
<a-form-item label="状态">
|
|
<a-radio-group v-model:value="formData.status">
|
|
<a-radio :value="1">正常</a-radio>
|
|
<a-radio :value="0">禁用</a-radio>
|
|
</a-radio-group>
|
|
</a-form-item>
|
|
</a-form>
|
|
</a-modal>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
import { message } from 'ant-design-vue'
|
|
import { UserOutlined, CheckCircleOutlined, StopOutlined, SafetyOutlined, SearchOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
|
import { getAdminList, createAdmin, updateAdmin, deleteAdmin, getAllRoles } from '@/api/system'
|
|
import { debounce } from 'lodash-es'
|
|
|
|
const loading = ref(false)
|
|
const tableData = ref([])
|
|
const allRoles = ref([])
|
|
const searchForm = reactive({ keyword: '', status: undefined })
|
|
const pagination = reactive({ current: 1, pageSize: 10, total: 0, showSizeChanger: true, showQuickJumper: true })
|
|
const stats = ref({ total: 0, active: 0, disabled: 0 })
|
|
|
|
const modalVisible = ref(false)
|
|
const submitting = ref(false)
|
|
const isEdit = ref(false)
|
|
const currentId = ref(null)
|
|
|
|
const formData = reactive({
|
|
username: '', password: '', realName: '', phone: '', email: '', roleIds: [], status: 1
|
|
})
|
|
|
|
const columns = [
|
|
{ title: 'ID', dataIndex: 'id', width: 70 },
|
|
{ title: '用户名', dataIndex: 'username', width: 120 },
|
|
{ title: '真实姓名', dataIndex: 'realName', width: 100 },
|
|
{ title: '邮箱', dataIndex: 'email', ellipsis: true },
|
|
{ title: '手机号', dataIndex: 'phone', width: 120 },
|
|
{ title: '角色', key: 'roles', width: 150 },
|
|
{ title: '状态', key: 'status', width: 80 },
|
|
{ title: '最后登录', dataIndex: 'lastLoginTime', width: 160 },
|
|
{ title: '操作', key: 'action', width: 180, fixed: 'right' }
|
|
]
|
|
|
|
const fetchRoles = async () => {
|
|
try {
|
|
const data = await getAllRoles()
|
|
allRoles.value = data || []
|
|
} catch (error) {}
|
|
}
|
|
|
|
const fetchData = async () => {
|
|
loading.value = true
|
|
try {
|
|
const data = await getAdminList({
|
|
...searchForm,
|
|
page: pagination.current,
|
|
pageSize: pagination.pageSize
|
|
})
|
|
tableData.value = data?.list || []
|
|
pagination.total = data?.total || 0
|
|
if (data?.stats) stats.value = data.stats
|
|
} catch (error) {}
|
|
finally { loading.value = false }
|
|
}
|
|
|
|
const handleSearch = debounce(() => {
|
|
pagination.current = 1
|
|
fetchData()
|
|
}, 300)
|
|
|
|
const handleReset = () => {
|
|
Object.assign(searchForm, { keyword: '', status: undefined })
|
|
pagination.current = 1
|
|
fetchData()
|
|
}
|
|
|
|
const handleTableChange = pag => {
|
|
pagination.current = pag.current
|
|
pagination.pageSize = pag.pageSize
|
|
fetchData()
|
|
}
|
|
|
|
const resetForm = () => {
|
|
Object.assign(formData, { username: '', password: '', realName: '', phone: '', email: '', roleIds: [], status: 1 })
|
|
}
|
|
|
|
const handleAdd = () => {
|
|
isEdit.value = false
|
|
currentId.value = null
|
|
resetForm()
|
|
modalVisible.value = true
|
|
}
|
|
|
|
const handleEdit = record => {
|
|
isEdit.value = true
|
|
currentId.value = record.id
|
|
Object.assign(formData, {
|
|
username: record.username,
|
|
password: '', // 编辑时密码留空
|
|
realName: record.realName,
|
|
phone: record.phone,
|
|
email: record.email,
|
|
roleIds: record.roles?.map(r => r.id) || [],
|
|
status: record.status
|
|
})
|
|
modalVisible.value = true
|
|
}
|
|
|
|
const handleSubmit = async () => {
|
|
if (!formData.username) { message.warning('请输入用户名'); return }
|
|
if (!isEdit.value && !formData.password) { message.warning('请输入密码'); return }
|
|
if (!formData.roleIds?.length) { message.warning('请选择角色'); return }
|
|
|
|
submitting.value = true
|
|
try {
|
|
if (isEdit.value) {
|
|
await updateAdmin(currentId.value, formData)
|
|
message.success('更新成功')
|
|
} else {
|
|
await createAdmin(formData)
|
|
message.success('创建成功')
|
|
}
|
|
modalVisible.value = false
|
|
fetchData()
|
|
} catch (error) {}
|
|
finally { submitting.value = false }
|
|
}
|
|
|
|
const handleDelete = async record => {
|
|
try {
|
|
await deleteAdmin(record.id)
|
|
message.success('删除成功')
|
|
fetchData()
|
|
} catch (error) {}
|
|
}
|
|
|
|
onMounted(() => {
|
|
fetchRoles()
|
|
fetchData()
|
|
})
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
@import '@/styles/page-common.less';
|
|
</style>
|