<template>
|
<div class="nav-header">
|
<div class="logo-and-title">
|
<img
|
class="side-logo"
|
src="~@/assets/images/img.png"
|
alt="sidebar-logo"
|
/>
|
<span style="color: #607D8B; font-size: 26px;">{{
|
$store.state.projectName
|
}}</span>
|
</div>
|
<div class="right">
|
<div class="approval-button-wrapper" v-if="$store.state.is_superuser || $store.state.is_staff">
|
<el-badge :value="pendingCount" :hidden="pendingCount === 0" class="approval-badge">
|
<el-button type="success" size="small" icon="el-icon-s-check" @click="handleApproval">
|
注册用户审批
|
</el-button>
|
</el-badge>
|
</div>
|
<div style="float: right; color: #262626; margin-right: 35px;">
|
<el-dropdown trigger="click" @command="handleCommand">
|
<span class="el-dropdown-link">
|
<i class="el-icon-user"></i>
|
{{ $store.state.name }}
|
<i class="el-icon-arrow-down el-icon--right"></i>
|
</span>
|
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-item command="changePassword">
|
<i class="el-icon-edit"></i>修改密码
|
</el-dropdown-item>
|
<el-dropdown-item divided command="logout">
|
<i class="el-icon-switch-button"></i>注销登录
|
</el-dropdown-item>
|
</el-dropdown-menu>
|
</el-dropdown>
|
</div>
|
</div>
|
|
<!-- 密码修改弹窗 -->
|
<el-dialog
|
title="修改密码"
|
:visible.sync="passwordDialogVisible"
|
width="30%"
|
:modal="false"
|
:lock-scroll="false"
|
:close-on-click-modal="false"
|
>
|
<el-form :model="passwordForm" :rules="passwordRules" ref="passwordForm">
|
<el-form-item prop="old_password">
|
<el-input
|
v-model="passwordForm.old_password"
|
type="password"
|
placeholder="请输入旧密码"
|
show-password
|
></el-input>
|
</el-form-item>
|
<el-form-item prop="new_password">
|
<el-input
|
v-model="passwordForm.new_password"
|
type="password"
|
placeholder="请输入新密码(至少6位)"
|
show-password
|
></el-input>
|
</el-form-item>
|
<el-form-item prop="confirm_password">
|
<el-input
|
v-model="passwordForm.confirm_password"
|
type="password"
|
placeholder="请确认新密码"
|
show-password
|
></el-input>
|
</el-form-item>
|
</el-form>
|
<span slot="footer">
|
<el-button @click="passwordDialogVisible = false">取 消</el-button>
|
<el-button type="primary" @click="submitPasswordChange">确 定</el-button>
|
</span>
|
</el-dialog>
|
|
<el-drawer
|
title="注册用户审批"
|
:visible.sync="approvalDrawerVisible"
|
direction="rtl"
|
size="60%"
|
:modal="false"
|
:append-to-body="true"
|
>
|
<div class="approval-container">
|
<el-table
|
:data="approvalData"
|
v-loading="approvalLoading"
|
stripe
|
style="width: 100%"
|
>
|
<el-table-column prop="username" label="用户名" width="120"></el-table-column>
|
<el-table-column prop="name" label="姓名" width="120"></el-table-column>
|
<el-table-column prop="phone" label="手机号" width="120"></el-table-column>
|
<el-table-column label="所属分组" width="200">
|
<template slot-scope="scope">
|
<el-tag
|
v-for="group in scope.row.groups"
|
:key="group"
|
size="mini"
|
style="margin-right: 3px"
|
>
|
{{ group }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="状态" width="100">
|
<template slot-scope="scope">
|
<el-tag :type="getStatusType(scope.row.status)">
|
{{ scope.row.status_display }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="注册时间" width="180">
|
<template slot-scope="scope">
|
{{ scope.row.date_joined | datetimeFormat }}
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" fixed="right" width="200">
|
<template slot-scope="scope">
|
<el-button
|
type="success"
|
size="mini"
|
icon="el-icon-check"
|
@click="handleApprove(scope.row, 'approve')"
|
>
|
通过
|
</el-button>
|
<el-button
|
type="danger"
|
size="mini"
|
icon="el-icon-close"
|
@click="handleApprove(scope.row, 'reject')"
|
>
|
不通过
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
<div v-if="!approvalLoading && approvalData.length === 0" class="empty-tip">
|
<el-empty description="暂无待审批用户"></el-empty>
|
</div>
|
</div>
|
</el-drawer>
|
</div>
|
</template>
|
|
<script>
|
import { changePassword, approveUser } from '@/api/user'
|
import { getUserApprovalList } from '@/api/user'
|
|
export default {
|
data() {
|
const validateConfirm = (rule, value, callback) => {
|
if (value !== this.passwordForm.new_password) {
|
callback(new Error('两次输入密码不一致!'));
|
} else {
|
callback();
|
}
|
};
|
|
return {
|
passwordDialogVisible: false,
|
approvalDrawerVisible: false,
|
approvalLoading: false,
|
approvalData: [],
|
pendingCount: 0,
|
passwordForm: {
|
old_password: '',
|
new_password: '',
|
confirm_password: ''
|
},
|
passwordRules: {
|
old_password: [
|
{ required: true, message: '请输入旧密码', trigger: 'blur' }
|
],
|
new_password: [
|
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
{ min: 6, message: '密码长度至少6位', trigger: 'blur' }
|
],
|
confirm_password: [
|
{ required: true, message: '请确认新密码', trigger: 'blur' },
|
{ validator: validateConfirm, trigger: 'blur' }
|
]
|
}
|
}
|
},
|
methods: {
|
addBadgeAnimation() {
|
if (this.pendingCount > 0) {
|
const badge = document.querySelector('.approval-badge .el-badge__content');
|
if (badge) {
|
badge.classList.add('badge-enter');
|
setTimeout(() => {
|
badge.classList.remove('badge-enter');
|
}, 1000);
|
}
|
}
|
},
|
getStatusType(status) {
|
const statusMap = {
|
'pending': 'warning',
|
'approved': 'success',
|
'rejected': 'danger'
|
};
|
return statusMap[status] || 'info';
|
},
|
handleCommand(command) {
|
if (command === 'changePassword') {
|
// 重置表单数据和验证状态
|
this.passwordForm = {
|
old_password: '',
|
new_password: '',
|
confirm_password: ''
|
}
|
this.$nextTick(() => {
|
if (this.$refs.passwordForm) {
|
this.$refs.passwordForm.clearValidate()
|
}
|
})
|
this.passwordDialogVisible = true
|
} else if (command === 'logout') {
|
this.$store.commit("isLogin", null);
|
this.setLocalValue("token", null);
|
this.setLocalValue("is_superuser", false);
|
this.setLocalValue("is_staff", false);
|
this.setLocalValue("show_hosts", false);
|
this.$store.commit("setProjectName", "");
|
this.$router.push({ name: "Login" });
|
}
|
},
|
handleApproval() {
|
this.approvalDrawerVisible = true;
|
this.getApprovalList();
|
},
|
getApprovalList() {
|
this.approvalLoading = true;
|
getUserApprovalList().then(resp => {
|
this.approvalData = resp.results || resp;
|
this.pendingCount = this.approvalData.length;
|
this.approvalLoading = false;
|
if (this.pendingCount > 0) {
|
this.addBadgeAnimation();
|
}
|
}).catch(() => {
|
this.approvalLoading = false;
|
});
|
},
|
handleApprove(row, action) {
|
const actionText = action === 'approve' ? '通过' : '不通过';
|
this.$confirm(`确定要${actionText}该用户的注册申请吗?`, "提示", {
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning"
|
}).then(() => {
|
this.$api.approveUser(row.id, { action: action }).then(() => {
|
this.$message.success(`用户审批已${actionText}`);
|
this.pendingCount = Math.max(0, this.pendingCount - 1);
|
this.getApprovalList();
|
});
|
}).catch(() => {});
|
},
|
submitPasswordChange() {
|
this.$refs.passwordForm.validate(valid => {
|
if (valid) {
|
this.$loading({ text: '正在修改密码...' })
|
changePassword(this.passwordForm)
|
.then(res => {
|
this.$loading().close()
|
if (res.code === "0000") {
|
this.$message.success('密码修改成功,请重新登录')
|
this.passwordDialogVisible = false
|
// 重置表单数据
|
this.passwordForm = {
|
old_password: '',
|
new_password: '',
|
confirm_password: ''
|
}
|
localStorage.removeItem('token')
|
localStorage.removeItem('user')
|
// 添加100ms延迟确保状态清除完成
|
setTimeout(() => {
|
this.$router.push({ path: '/lunarlink/login' })
|
}, 100)
|
} else {
|
this.$message.error(res.msg || '密码修改失败')
|
}
|
})
|
.catch(error => {
|
this.$loading().close()
|
const errorMsg = (error.response && error.response.data && error.response.data.msg) || '请求失败,请稍后重试'
|
this.$message.error(errorMsg)
|
})
|
}
|
})
|
}
|
},
|
mounted() {
|
if (this.$store.state.is_superuser || this.$store.state.is_staff) {
|
this.getApprovalList();
|
setTimeout(() => {
|
this.addBadgeAnimation();
|
}, 500);
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
.right {
|
position: fixed;
|
left: 300px;
|
right: 0;
|
top: 0;
|
}
|
|
.right div a:hover {
|
color: darkcyan;
|
}
|
|
.nav-header {
|
position: fixed;
|
left: 0;
|
right: 0;
|
z-index: 100;
|
background: #ffffff;
|
margin: 0 auto;
|
font-size: 14px;
|
width: 100%;
|
height: 49px;
|
line-height: 49px;
|
}
|
|
.side-logo {
|
width: 100px;
|
height: 36px;
|
padding-left: 10px;
|
vertical-align: top;
|
}
|
|
.logo-and-title {
|
display: flex;
|
align-items: center;
|
}
|
|
.a-text {
|
margin-right: 10px;
|
font-size: 15px;
|
color: #d9d9d9;
|
}
|
/* 确保下拉菜单样式 */
|
.el-dropdown-link {
|
cursor: pointer;
|
color: #409EFF;
|
padding: 0 15px;
|
}
|
|
.approval-container {
|
padding: 20px;
|
}
|
|
.empty-tip {
|
text-align: center;
|
padding: 40px 0;
|
}
|
|
.approval-button-wrapper {
|
float: right;
|
color: #262626;
|
margin-right: 15px;
|
display: flex;
|
align-items: center;
|
height: 49px;
|
position: relative;
|
}
|
|
/* 优化按钮本身 */
|
.approval-button-wrapper .el-button {
|
position: relative;
|
padding: 7px 12px;
|
border-radius: 6px;
|
font-weight: 500;
|
transition: all 0.3s ease;
|
box-shadow: 0 2px 8px rgba(103, 194, 58, 0.2);
|
}
|
|
.approval-button-wrapper .el-button:hover {
|
transform: translateY(-1px);
|
box-shadow: 0 4px 12px rgba(103, 194, 58, 0.3);
|
}
|
|
.approval-button-wrapper .el-button:active {
|
transform: translateY(0);
|
}
|
|
/* 徽章优化 - 更自然的设计 */
|
.approval-badge {
|
position: relative;
|
display: inline-flex;
|
align-items: center;
|
}
|
|
.approval-badge ::v-deep .el-badge__content {
|
position: absolute !important;
|
top: -8px !important;
|
right: -8px !important;
|
height: 20px;
|
min-width: 20px;
|
line-height: 20px;
|
padding: 0 4px;
|
font-size: 12px;
|
font-weight: 600;
|
border-radius: 10px;
|
background: linear-gradient(135deg, #ff6b6b, #ff4757);
|
color: white;
|
border: 2px solid white;
|
box-shadow: 0 3px 6px rgba(255, 107, 107, 0.3);
|
z-index: 10;
|
animation: badge-pulse 2s infinite;
|
}
|
|
/* 徽章脉冲动画 */
|
@keyframes badge-pulse {
|
0% {
|
box-shadow: 0 0 0 0 rgba(255, 107, 107, 0.4);
|
}
|
70% {
|
box-shadow: 0 0 0 4px rgba(255, 107, 107, 0);
|
}
|
100% {
|
box-shadow: 0 0 0 0 rgba(255, 107, 107, 0);
|
}
|
}
|
|
/* 徽章入场动画 */
|
@keyframes badge-bounce {
|
0% {
|
transform: scale(0);
|
}
|
50% {
|
transform: scale(1.2);
|
}
|
100% {
|
transform: scale(1);
|
}
|
}
|
|
.approval-badge ::v-deep .el-badge__content.badge-enter {
|
animation: badge-bounce 0.5s ease;
|
}
|
|
/* 当徽章值为0时的隐藏效果 */
|
.approval-badge ::v-deep .el-badge__content.is-fixed.is-dot {
|
top: -4px !important;
|
right: -4px !important;
|
height: 8px;
|
width: 8px;
|
min-width: 8px;
|
border: 1px solid white;
|
background: linear-gradient(135deg, #ffa502, #ff7f00);
|
}
|
|
/* 可选:添加图标徽章效果 */
|
.approval-button-wrapper .el-button i {
|
position: relative;
|
margin-right: 4px;
|
}
|
|
/* 添加按钮渐变效果 */
|
.approval-button-wrapper .el-button--success {
|
background: linear-gradient(135deg, #85d27a, #67c23a);
|
border: none;
|
}
|
|
.approval-button-wrapper .el-button--success:hover {
|
background: linear-gradient(135deg, #7ac76f, #5da534);
|
}
|
|
/* 徽章数字特殊样式 */
|
.approval-badge ::v-deep .el-badge__content .el-badge__inner {
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
letter-spacing: -0.5px;
|
}
|
|
/* 调整整体布局 */
|
.right {
|
position: fixed;
|
left: 300px;
|
right: 0;
|
top: 0;
|
display: flex;
|
align-items: center;
|
justify-content: flex-end;
|
padding-right: 10px;
|
gap: 10px; /* 增加元素间距 */
|
}
|
|
/* 用户下拉菜单样式优化 */
|
.el-dropdown-link {
|
cursor: pointer;
|
color: #409EFF;
|
padding: 8px 16px;
|
border-radius: 6px;
|
transition: all 0.3s ease;
|
display: inline-flex;
|
align-items: center;
|
}
|
|
.el-dropdown-link:hover {
|
background-color: rgba(64, 158, 255, 0.1);
|
}
|
|
.el-dropdown-link i {
|
margin-right: 4px;
|
}
|
|
</style>
|