Files
vue3-element-admin/src/views/system/user/index.vue

705 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="app-container">
<el-row :gutter="20">
<!-- 部门数据 -->
<el-col
:span="4"
:xs="24"
>
<el-card class="box-card">
<el-input
v-model="deptName"
placeholder="部门名称"
clearable
size="small"
:prefix-icon="Search"
style="margin-bottom: 20px"
/>
<el-tree
ref="deptTreeRef"
:data="deptOptions"
:props="{ children: 'children',label: 'label',disabled:''}"
:expand-on-click-node="false"
:filter-node-method="filterDeptNode"
default-expand-all
@node-click="handleDeptNodeClick"
>
</el-tree>
</el-card>
</el-col>
<!-- 用户数据 -->
<el-col
:span="20"
:xs="24"
>
<el-card class="box-card">
<el-form
ref="queryFormRef"
:model="queryParams"
:inline="true"
>
<el-form-item>
<el-button
type="success"
:icon="Plus"
size="mini"
@click="handleAdd"
>
新增
</el-button>
<el-button
type="primary"
:icon="Edit"
size="mini"
:disabled="single"
@click="handleUpdate"
>
修改
</el-button>
<el-button
type="danger"
plain
:icon="Delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
>
删除
</el-button>
</el-form-item>
<el-form-item prop="keywords">
<el-input
v-model="queryParams.keywords"
placeholder="用户名/昵称/手机号"
clearable
size="small"
style="width: 200px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item prop="status">
<el-select
v-model="queryParams.status"
placeholder="用户状态"
clearable
size="small"
style="width: 200px"
>
<el-option label="正常" value="1"/>
<el-option label="停用" value="0"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
:icon="Search"
size="mini"
@click="handleQuery"
>
搜索
</el-button>
<el-button
:icon="Refresh"
size="mini"
@click="resetQuery"
>
重置
</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="loading"
:data="pageList"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
width="50"
align="center"
/>
<el-table-column
key="id"
label="用户编号"
align="center"
prop="id"
/>
<el-table-column
key="username"
label="用户名称"
align="center"
prop="username"
:show-overflow-tooltip="true"
/>
<el-table-column
key="nickname"
label="用户昵称"
align="center"
prop="nickname"
:show-overflow-tooltip="true"
/>
<el-table-column
key="deptName"
label="部门"
align="center"
prop="deptName"
:show-overflow-tooltip="true"
/>
<el-table-column
key="mobile"
label="手机号码"
align="center"
prop="mobile"
width="120"
/>
<el-table-column
key="status"
label="状态"
align="center"
prop="status"
>
<template #default="scope">
<el-switch
v-model="scope.row.status"
:inactive-value=0
:active-value=1
@change="handleStatusChange(scope.row)"
/>
</template>
</el-table-column>
<el-table-column
label="创建时间"
align="center"
prop="gmtCreate"
width="180"
>
</el-table-column>
<el-table-column
label="操作"
align="center"
width="150"
>
<template #default="scope">
<el-button
type="primary"
:icon="Edit"
size="mini"
circle
plain
@click="handleUpdate(scope.row)"
>
</el-button>
<el-button
type="danger"
size="mini"
:icon="Delete"
circle
plain
@click="handleDelete(scope.row)"
>
</el-button>
<el-button
type="warning"
size="mini"
:icon="Lock"
circle
plain
@click="resetPassword(scope.row)"
>
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
v-model:page="queryParams.page"
v-model:limit="queryParams.limit"
@pagination="handleQuery"
/>
</el-card>
</el-col>
</el-row>
<!-- 添加或修改参数配置对话框 -->
<el-dialog
:title="dialog.title"
v-model="dialog.visible"
width="600px"
append-to-body
@close="cancel"
>
<el-form
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="80px"
>
<el-form-item
label="用户名"
prop="username"
>
<el-input
:readonly="!formData.id"
v-model="formData.username"
placeholder="请输入用户名"
/>
</el-form-item>
<el-form-item
label="用户昵称"
prop="nickname"
>
<el-input
v-model="formData.nickname"
placeholder="请输入用户昵称"
/>
</el-form-item>
<el-form-item
label="归属部门"
prop="deptId"
>
<tree-select
v-model="formData.deptId"
:options="deptOptions"
placeholder="请选择归属部门"
/>
</el-form-item>
<el-form-item
label="手机号码"
prop="mobile"
>
<el-input
v-model="formData.mobile"
placeholder="请输入手机号码"
maxlength="11"
/>
</el-form-item>
<el-form-item
label="邮箱"
prop="email"
>
<el-input
v-model="formData.email"
placeholder="请输入邮箱"
maxlength="50"
/>
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="formData.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="用户性别">
<el-select
v-model="formData.gender"
placeholder="请选择"
>
<el-option label="未知" :value="0"/>
<el-option label="男" :value="1"/>
<el-option label="女" :value="2"/>
</el-select>
</el-form-item>
<el-form-item label="角色">
<el-select
v-model="formData.roleIds"
multiple
placeholder="请选择"
>
<el-option
v-for="item in roleOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="formData.id === undefined"
label="用户密码"
prop="password"
>
<el-input
v-model="formData.password"
placeholder="请输入用户密码"
type="password"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button
type="primary"
@click="submitForm"
>
</el-button>
<el-button @click="cancel">
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang='ts'>
// Vue依赖
import {reactive, ref, unref, watchEffect, onMounted, getCurrentInstance, toRefs} from 'vue'
// API依赖
import {listUsersWithPage, getUserFormDetail, deleteUsers, addUser, updateUser, updateUserPart} from '@/api/system/user'
import {listDeptSelect} from '@/api/system/dept'
import {listRoles} from '@/api/system/role'
// 组件依赖
import {ElMessage, ElMessageBox, ElTree, ElForm} from 'element-plus'
import {Search, Plus, Edit, Refresh, Delete, Lock} from '@element-plus/icons'
import TreeSelect from '@/components/TreeSelect/Index.vue'
const deptTreeRef = ref(ElTree)
const dataFormRef = ref(ElForm)
const queryFormRef = ref(ElForm) // 变量名和绑定名ref一致
const state = reactive({
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 总条数
total: 0,
// 用户表格数据
pageList: [],
// 弹窗属性
dialog: {
title: '',
visible: false
},
// 部门树选项
deptOptions: [],
// 部门名称
deptName: '',
// 性别状态字典
genderOptions: [] as any[],
// 角色选项
roleOptions: [] as any[],
// 表单参数
formData: {
id: undefined,
deptId: undefined,
username: undefined,
nickname: undefined,
password: '',
mobile: undefined,
email: undefined,
gender: undefined,
status: 1,
remark: undefined,
roleIds: []
},
// 查询参数
queryParams: {
page: 1,
limit: 10,
keywords: undefined,
status: undefined,
deptId: undefined
},
// 表单校验
rules: {
username: [
{required: true, message: '用户名不能为空', trigger: 'blur'}
],
password: [
{required: true, message: '用户密码不能为空', trigger: 'blur'}
],
roleId: [
{required: true, message: '用户角色不能为空', trigger: 'blur'}
],
deptId: [
{required: true, message: '归属部门不能为空', trigger: 'blur'}
],
email: [
{
type: 'email',
message: '\'请输入正确的邮箱地址',
trigger: ['blur', 'change']
}
],
mobile: [
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}
]
}
})
const {
loading,
single,
multiple,
queryParams,
pageList,
total,
dialog,
formData,
rules,
deptName,
deptOptions,
roleOptions
} = toRefs(state)
/**
* 加载部门数据
**/
async function loadDeptOptions() {
listDeptSelect().then(response => {
state.deptOptions = response.data
})
}
/**
* 部门筛选
**/
watchEffect(() => {
const deptTree = unref(deptTreeRef)
deptTree.filter(state.deptName)
}, {
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行
})
function filterDeptNode(value: string, data: any) {
if (!value) {
return true
}
return data.label.indexOf(value) !== -1
}
/**
* 部门树点击
**/
function handleDeptNodeClick(data: { [key: string]: any }) {
state.queryParams.deptId = data.id
handleQuery()
}
/**
* 加载角色数据
*/
async function loadRoleOptions() {
listRoles().then(response => {
state.roleOptions = response.data
})
}
/**
* 用户状态修改
**/
function handleStatusChange(row: { [key: string]: any }) {
const text = row.status === 1 ? '启用' : '停用'
ElMessageBox.confirm('确认要' + text + '' + row.username + '用户吗?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return updateUserPart(row.id, {status: row.status})
}).then(() => {
ElMessage.success(text + '成功')
}).catch(() => {
row.status = row.status === 1 ? 0 : 1
})
}
/**
* 密码重置
**/
function resetPassword(row: { [key: string]: any }) {
ElMessageBox.prompt('请输入"' + row.username + '"的新密码', '修改密码', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(({value}) => {
if (!value) {
ElMessage.warning("请输入新密码")
return false
}
updateUserPart(row.id, {
password: value
}).then(() => {
ElMessage.success('修改成功,新密码是:' + value)
})
}).catch(() => {
})
}
/**
* 用户查询
**/
function handleQuery() {
state.loading = true
listUsersWithPage(state.queryParams).then(response => {
const {data, total} = response as any
state.pageList = data
state.total = total
state.loading = false
})
}
/**
* 重置查询
*/
function resetQuery() {
const queryForm = unref(queryFormRef)
queryForm.resetFields()
handleQuery()
}
/**
* 表格行选中事件
**/
function handleSelectionChange(selection: any) {
state.ids = selection.map((item: any) => item.id)
state.single = selection.length !== 1
state.multiple = !selection.length
}
/**
* 添加用户
**/
async function handleAdd() {
resetForm()
await loadDeptOptions()
await loadRoleOptions()
state.dialog = {
title: '添加用户',
visible: true
}
}
/**
* 修改用户
**/
async function handleUpdate(row: { [key: string]: any }) {
const userId = row.id || state.ids
await loadDeptOptions()
await loadRoleOptions()
state.dialog = {
title: '修改用户',
visible: true
}
getUserFormDetail(userId).then((response: any) => {
state.formData = response.data
})
}
/**
* 表单提交
**/
function submitForm() {
const dataForm = unref(dataFormRef)
dataForm.validate((valid: any) => {
if (valid) {
const userId = state.formData.id
if (userId) {
updateUser(userId, state.formData).then(() => {
ElMessage.success('修改成功')
state.dialog.visible = false
handleQuery()
})
} else {
addUser(state.formData).then((response: any) => {
ElMessage.success('新增成功')
state.dialog.visible = false
handleQuery()
})
}
}
})
}
/**
* 表单重置
**/
function resetForm() {
state.formData = {
id: undefined,
deptId: undefined,
username: undefined,
nickname: undefined,
password: '',
mobile: undefined,
email: undefined,
gender: undefined,
status: 1,
remark: undefined,
roleIds: []
}
}
/**
* 删除用户
**/
function handleDelete(row: { [key: string]: any }) {
const userIds = row.id || state.ids.join(',')
ElMessageBox.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(function () {
deleteUsers(userIds).then(() => {
ElMessage.success('删除成功')
handleQuery()
})
}).catch(() =>
ElMessage.info('已取消删除')
)
}
/**
* 取消关闭
*/
function cancel() {
resetForm()
state.dialog.visible = false
}
onMounted(() => {
loadDeptOptions()
handleQuery()
const {proxy}: any = getCurrentInstance();
proxy.$listDictsByCode('gender').then((response: any) => {
state.genderOptions = response?.data
})
})
</script>
<style lang="scss" scoped>
</style>