| | |
| | | }}</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"> |
| | |
| | | {{ $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-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 } from '@/api/user' |
| | | import { changePassword, approveUser } from '@/api/user' |
| | | import { getUserApprovalList } from '@/api/user' |
| | | |
| | | export default { |
| | | data() { |
| | |
| | | |
| | | return { |
| | | passwordDialogVisible: false, |
| | | approvalDrawerVisible: false, |
| | | approvalLoading: false, |
| | | approvalData: [], |
| | | pendingCount: 0, |
| | | passwordForm: { |
| | | old_password: '', |
| | | new_password: '', |
| | |
| | | } |
| | | }, |
| | | 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.$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 => { |
| | |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | mounted() { |
| | | if (this.$store.state.is_superuser || this.$store.state.is_staff) { |
| | | this.getApprovalList(); |
| | | setTimeout(() => { |
| | | this.addBadgeAnimation(); |
| | | }, 500); |
| | | } |
| | | } |
| | | } |
| | |
| | | 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> |