<!--
|
- Copyright (c) 2018-2025, ztt All rights reserved.
|
-
|
- Redistribution and use in source and binary forms, with or without
|
- modification, are permitted provided that the following conditions are met:
|
-
|
- Redistributions of source code must retain the above copyright notice,
|
- this list of conditions and the following disclaimer.
|
- Redistributions in binary form must reproduce the above copyright
|
- notice, this list of conditions and the following disclaimer in the
|
- documentation and/or other materials provided with the distribution.
|
- Neither the name of the pig4cloud.com developer nor the names of its
|
- contributors may be used to endorse or promote products derived from
|
- this software without specific prior written permission.
|
- Author: ztt
|
-->
|
|
<template>
|
<div class="user">
|
<basic-container>
|
<el-row :span="24">
|
<el-col :xs="24" :sm="24" :md="5" class="user__tree">
|
<avue-tree :option="treeOption" :data="treeData" @node-click="nodeClick">
|
<span class="el-tree-node__label" slot-scope="{ node, data }">
|
<el-tooltip class="item" effect="dark" content="无数据权限" placement="right-start" v-if="data.isLock">
|
<span>{{ node.label }} <i class="el-icon-lock"></i></span>
|
</el-tooltip>
|
<span v-if="!data.isLock">{{ node.label }}</span>
|
</span>
|
</avue-tree>
|
</el-col>
|
<el-col :xs="24" :sm="24" :md="19" class="user__main">
|
<avue-crud ref="crud" :option="option" v-model="form" :page="page" :table-loading="listLoading" :before-open="handleOpenBefore" :data="list" @on-load="getList" @search-change="searchChange" @refresh-change="refreshChange" @size-change="sizeChange" @current-change="currentChange" @row-update="update" @row-save="create">
|
<template slot="menuLeft">
|
<el-button v-if="sys_user_add" class="filter-item" type="primary" size="small" icon="el-icon-edit" @click="$refs.crud.rowAdd()">添加 </el-button>
|
</template>
|
<template slot="username" slot-scope="scope">
|
<span>{{ scope.row.username }}</span>
|
</template>
|
<template slot="role" slot-scope="scope">
|
<span v-for="(role, index) in scope.row.roleList" :key="index">
|
<el-tag>{{ role.roleName }} </el-tag>
|
</span>
|
</template>
|
<template slot="deptId" slot-scope="scope">
|
{{ scope.row.deptName }}
|
</template>
|
<template slot="lockFlag" slot-scope="scope">
|
<el-tag>{{ scope.label }}</el-tag>
|
</template>
|
<template slot="menu" slot-scope="scope">
|
<el-button v-if="sys_user_edit" type="text" size="small" icon="el-icon-edit" @click="handleUpdate(scope.row, scope.index)">编辑 </el-button>
|
<el-button v-if="sys_user_del" type="text" size="small" icon="el-icon-delete" @click="deletes(scope.row, scope.index)">删除 </el-button>
|
<el-button v-if="sys_user_lock" type="text" size="small" icon="el-icon-unlock" @click="unlock(scope.row, scope.index)">解锁 </el-button>
|
</template>
|
<template slot="deptIdForm" slot-scope="scope">
|
<avue-input-tree v-model="form.deptId" :node-click="getNodeData" :dic="treeDeptData" :props="defaultProps" placeholder="请选择所属分组" />
|
</template>
|
<template slot="staffInfoForm" slot-scope="scope">
|
<el-input @focus="showStaff = true" v-model="form.staffInfo" readonly>
|
<i class="el-icon-arrow-down el-input__icon" slot="suffix" @click="showStaff = true"></i>
|
</el-input>
|
</template>
|
<template slot="roleForm" slot-scope="scope">
|
<avue-select v-model="role" :dic="rolesOptions" :props="roleProps" multiple placeholder="请选择角色" />
|
</template>
|
<template slot="passwordForm" slot-scope="scope">
|
<div class="password-input-container">
|
<el-input v-model="form.password" type="password" @input="checkPasswordStrength" placeholder="请输入密码" show-password style="width: 100%"> </el-input>
|
<div v-if="form.password" class="password-strength-indicator">
|
密码强度: <span :class="passwordLevelClass">{{ passwordLevel }}</span>
|
</div>
|
</div>
|
</template>
|
</avue-crud>
|
</el-col>
|
</el-row>
|
</basic-container>
|
|
<staffDialog :currshowlist.sync="showStaff" @listenToStaffEvent="selectStaff" />
|
</div>
|
</template>
|
<script>
|
import staffDialog from '@/views/common/staff.vue'
|
import { addObj, delObj, fetchList, putObj, unlock } from '@/api/admin/user'
|
import { deptRoleList } from '@/api/admin/role'
|
import { fetchTree } from '@/api/admin/dept'
|
import { tableOption } from '@/const/crud/admin/user'
|
import { mapGetters } from 'vuex'
|
import Template from '../../quality/parts/template'
|
import { getObj } from '@/api/basic/staff'
|
|
export default {
|
name: 'SysUser',
|
components: { Template, staffDialog },
|
data() {
|
return {
|
showStaff: false,
|
searchForm: {},
|
treeOption: {
|
nodeKey: 'id',
|
addBtn: false,
|
menu: false,
|
props: {
|
label: 'name',
|
value: 'id'
|
}
|
},
|
treeData: [],
|
option: tableOption,
|
treeDeptData: [],
|
checkedKeys: [],
|
roleProps: {
|
label: 'roleName',
|
value: 'roleId'
|
},
|
defaultProps: {
|
label: 'name',
|
value: 'id'
|
},
|
page: {
|
total: 0, // 总页数
|
currentPage: 1, // 当前页数
|
pageSize: 20, // 每页显示多少条,
|
isAsc: false // 是否倒序
|
},
|
list: [],
|
listLoading: true,
|
role: [],
|
form: {},
|
rolesOptions: [],
|
passwordLevel: '', // 密码强度级别
|
passwordLevelClass: '' // 密码强度样式类
|
}
|
},
|
computed: {
|
...mapGetters(['permissions'])
|
},
|
watch: {
|
role() {
|
this.form.role = this.role
|
}
|
},
|
created() {
|
this.sys_user_add = this.permissions.sys_user_add
|
this.sys_user_edit = this.permissions.sys_user_edit
|
this.sys_user_del = this.permissions.sys_user_del
|
this.sys_user_lock = this.permissions.sys_user_lock
|
this.init()
|
},
|
methods: {
|
selectStaff(staff) {
|
this.form.staffInfo = staff.staffName + '-' + staff.staffNo
|
this.form.phone = staff.phone
|
this.form.staffId = staff.id
|
},
|
init() {
|
fetchTree().then((response) => {
|
this.treeData = response.data.data
|
})
|
},
|
nodeClick(data) {
|
this.page.page = 1
|
this.getList(this.page, { deptId: data.id })
|
},
|
getList(page, params) {
|
this.listLoading = true
|
fetchList(
|
Object.assign(
|
{
|
current: page.currentPage,
|
size: page.pageSize
|
},
|
params,
|
this.searchForm
|
)
|
).then((response) => {
|
this.list = response.data.data.records
|
this.page.total = response.data.data.total
|
this.listLoading = false
|
})
|
},
|
getNodeData() {
|
deptRoleList().then((response) => {
|
this.rolesOptions = response.data.data
|
})
|
},
|
searchChange(param, done) {
|
this.searchForm = param
|
this.page.currentPage = 1
|
this.getList(this.page, param)
|
done()
|
},
|
sizeChange(pageSize) {
|
this.page.pageSize = pageSize
|
},
|
currentChange(current) {
|
this.page.currentPage = current
|
},
|
refreshChange() {
|
this.getList(this.page)
|
},
|
handleOpenBefore(show, type) {
|
window.boxType = type
|
// 查询部门树
|
fetchTree().then((response) => {
|
this.treeDeptData = response.data.data
|
})
|
// 查询角色列表
|
deptRoleList().then((response) => {
|
this.rolesOptions = response.data.data
|
})
|
|
if (['edit', 'views'].includes(type)) {
|
this.role = []
|
for (let i = 0; i < this.form.roleList.length; i++) {
|
this.role[i] = this.form.roleList[i].roleId
|
}
|
} else if (type === 'add') {
|
this.role = []
|
}
|
show()
|
},
|
handleUpdate(row, index) {
|
this.$refs.crud.rowEdit(row, index)
|
this.form.password = undefined
|
},
|
|
create(row, done, loading) {
|
// 检查密码强度
|
if (this.passwordLevel !== '强') {
|
this.$message.warning('密码强度不够,请确保密码包含:大小写字母、数字和特殊字符,且长度不少于8位')
|
loading()
|
return
|
}
|
|
if (this.form.phone.indexOf('*') > 0) {
|
this.form.phone = undefined
|
}
|
|
addObj(this.form)
|
.then(() => {
|
this.getList(this.page)
|
done()
|
this.$notify.success('创建成功')
|
})
|
.catch(() => {
|
loading()
|
})
|
},
|
update(row, index, done, loading) {
|
// 如果修改了密码,也要检查密码强度
|
if (this.form.password && this.passwordLevel !== '强') {
|
this.$message.warning('密码强度不够,请确保密码包含:大小写字母、数字和特殊字符,且长度不少于8位')
|
loading()
|
return
|
}
|
|
if (this.form.phone && this.form.phone.indexOf('*') > 0) {
|
this.form.phone = undefined
|
}
|
|
putObj(this.form)
|
.then(() => {
|
this.getList(this.page)
|
done()
|
this.$notify.success('修改成功')
|
})
|
.catch(() => {
|
loading()
|
})
|
},
|
deletes(row, index) {
|
this.$confirm('此操作将永久删除该用户(用户名:' + row.username + '), 是否继续?', '提示', {
|
confirmButtonText: '确定',
|
cancelButtonText: '取消',
|
closeOnClickModal: false,
|
type: 'warning'
|
}).then(() => {
|
delObj(row.userId)
|
.then(() => {
|
this.list.splice(index, 1)
|
this.$notify.success('删除成功')
|
})
|
.catch(() => {
|
this.$notify.error('删除失败')
|
})
|
})
|
},
|
unlock(row, index) {
|
unlock({ id: row.userId }).then((repsonse) => {})
|
},
|
// 检查密码强度
|
checkPasswordStrength(password) {
|
let strength = 0
|
|
// 检查长度
|
if (password.length >= 8) strength++
|
|
// 检查是否包含数字
|
if (/\d/.test(password)) strength++
|
|
// 检查是否包含小写字母
|
if (/[a-z]/.test(password)) strength++
|
|
// 检查是否包含大写字母
|
if (/[A-Z]/.test(password)) strength++
|
|
// 检查是否包含特殊字符
|
if (/[!@#$%^&*]/.test(password)) strength++
|
|
switch (strength) {
|
case 0:
|
case 1:
|
this.passwordLevel = '弱'
|
this.passwordLevelClass = 'password-weak'
|
break
|
case 2:
|
case 3:
|
this.passwordLevel = '中'
|
this.passwordLevelClass = 'password-medium'
|
break
|
case 4:
|
case 5:
|
this.passwordLevel = '强'
|
this.passwordLevelClass = 'password-strong'
|
break
|
}
|
}
|
}
|
}
|
</script>
|
<style lang="scss">
|
.user {
|
height: 100%;
|
|
&__tree {
|
padding-top: 3px;
|
padding-right: 20px;
|
}
|
|
&__main {
|
.el-card__body {
|
padding-top: 0;
|
}
|
}
|
}
|
|
.password-input-container {
|
width: 100%;
|
.el-input {
|
width: 100%;
|
}
|
}
|
|
.password-strength-indicator {
|
margin-top: 5px;
|
font-size: 12px;
|
color: #606266;
|
}
|
|
.password-weak {
|
color: #f56c6c;
|
}
|
|
.password-medium {
|
color: #e6a23c;
|
}
|
|
.password-strong {
|
color: #67c23a;
|
}
|
</style>
|