Compare commits
4 Commits
74034ed26a
...
0d1127f25e
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d1127f25e | |||
| bb7b4035fc | |||
| c3b3ed3681 | |||
| 3fe027661b |
@@ -0,0 +1,12 @@
|
||||
package com.corewing.app.dto.biz.user;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BizUserBatchDeleteRequest {
|
||||
|
||||
private List<Long> ids;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.corewing.app.dto.biz.user;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class BizUserIdRequest {
|
||||
private long id;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.corewing.app.dto.biz.user;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BizUserStatusRequest {
|
||||
private List<Long> ids;
|
||||
private Integer status;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.corewing.app.dto.biz;
|
||||
package com.corewing.app.dto.biz.user;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ResetPassword {
|
||||
public class ResetPasswordRequest {
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
@@ -2,15 +2,17 @@ package com.corewing.app.modules.admin.biz;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.corewing.app.common.Result;
|
||||
import com.corewing.app.dto.biz.ResetPassword;
|
||||
import com.corewing.app.entity.SysUser;
|
||||
import com.corewing.app.dto.biz.user.BizUserBatchDeleteRequest;
|
||||
import com.corewing.app.dto.biz.user.BizUserIdRequest;
|
||||
import com.corewing.app.dto.biz.user.BizUserStatusRequest;
|
||||
import com.corewing.app.dto.biz.user.ResetPasswordRequest;
|
||||
import com.corewing.app.entity.User;
|
||||
import com.corewing.app.service.UserService;
|
||||
import io.lettuce.core.ConnectionEvents;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/biz/user")
|
||||
@@ -42,15 +44,63 @@ public class BizUserController {
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param resetPassword 修改密码DTO
|
||||
* @param resetPasswordRequest 修改密码DTO
|
||||
* @return 成功 or 失败
|
||||
*/
|
||||
@PutMapping("/resetPassword")
|
||||
@ResponseBody
|
||||
public Result<String> resetPassword(@RequestBody ResetPassword resetPassword) {
|
||||
boolean flag = userService.resetPassword(resetPassword);
|
||||
public Result<String> resetPassword(@RequestBody ResetPasswordRequest resetPasswordRequest) {
|
||||
boolean flag = userService.resetPassword(resetPasswordRequest);
|
||||
return Result.isBoolAsMsg(flag, "修改密码成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/save")
|
||||
@ResponseBody
|
||||
public Result<String> save(@RequestBody User user) {
|
||||
return Result.isBool(userService.save(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/update")
|
||||
@ResponseBody
|
||||
public Result<String> update(@RequestBody User user) {
|
||||
return Result.isBool(userService.update(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单删用户
|
||||
* @param bizUserIdRequest
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/delete")
|
||||
@ResponseBody
|
||||
public Result<String> delete(@RequestBody BizUserIdRequest bizUserIdRequest) {
|
||||
return Result.isBool(userService.removeById(bizUserIdRequest.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除用户
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/batchDelete")
|
||||
@ResponseBody
|
||||
public Result<String> batchDelete(@RequestBody BizUserBatchDeleteRequest bizUserBatchDeleteRequest) {
|
||||
return Result.isBool(userService.removeByIds(bizUserBatchDeleteRequest.getIds()));
|
||||
}
|
||||
|
||||
@PostMapping("/batchStatus")
|
||||
@ResponseBody
|
||||
public Result<String> batchStatus(@RequestBody BizUserStatusRequest bizUserStatusRequests) {
|
||||
return Result.isBool(userService.batchStatus(bizUserStatusRequests));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,17 +2,39 @@ package com.corewing.app.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.corewing.app.dto.biz.ResetPassword;
|
||||
import com.corewing.app.entity.SysUser;
|
||||
import com.corewing.app.dto.biz.user.BizUserIdRequest;
|
||||
import com.corewing.app.dto.biz.user.BizUserStatusRequest;
|
||||
import com.corewing.app.dto.biz.user.ResetPasswordRequest;
|
||||
import com.corewing.app.entity.User;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 应用用户 Service 接口
|
||||
*/
|
||||
public interface UserService extends IService<User> {
|
||||
|
||||
/**
|
||||
* 获取用户分页数据
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
Page<User> page(User user);
|
||||
|
||||
/**
|
||||
* 新增用户数据
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
boolean save(User user);
|
||||
|
||||
/**
|
||||
* 更新用户数据
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
boolean update(User user);
|
||||
|
||||
/**
|
||||
* 根据用户名查询用户
|
||||
*
|
||||
@@ -82,8 +104,15 @@ public interface UserService extends IService<User> {
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param resetPassword
|
||||
* @param resetPasswordRequest
|
||||
* @return
|
||||
*/
|
||||
boolean resetPassword(ResetPassword resetPassword);
|
||||
boolean resetPassword(ResetPasswordRequest resetPasswordRequest);
|
||||
|
||||
/**
|
||||
* 批量修改状态
|
||||
* @param bizUserStatusRequest
|
||||
* @return
|
||||
*/
|
||||
boolean batchStatus(BizUserStatusRequest bizUserStatusRequest);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,14 @@ package com.corewing.app.service.impl;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.corewing.app.common.page.PageContext;
|
||||
import com.corewing.app.dto.biz.ResetPassword;
|
||||
import com.corewing.app.dto.biz.user.BizUserIdRequest;
|
||||
import com.corewing.app.dto.biz.user.BizUserStatusRequest;
|
||||
import com.corewing.app.dto.biz.user.ResetPasswordRequest;
|
||||
import com.corewing.app.entity.User;
|
||||
import com.corewing.app.mapper.UserMapper;
|
||||
import com.corewing.app.service.UserService;
|
||||
@@ -13,10 +17,13 @@ import com.corewing.app.service.VerifyCodeService;
|
||||
import com.corewing.app.util.I18nUtil;
|
||||
import com.corewing.app.util.Ip2RegionUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 应用用户 Service 实现类
|
||||
@@ -44,6 +51,48 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
return page(page, queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean save(User user) {
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(User::getUsername, user.getUsername());
|
||||
if(count(queryWrapper) > 0) {
|
||||
throw new RuntimeException(I18nUtil.getMessage(I18nUtil.getMessage("error.username.exists")));
|
||||
}
|
||||
|
||||
|
||||
return super.save(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(User user) {
|
||||
|
||||
// 校验用户名
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(User::getUsername, user.getUsername());
|
||||
queryWrapper.ne(User::getId, user.getId());
|
||||
if(count(queryWrapper) > 0) {
|
||||
throw new RuntimeException(I18nUtil.getMessage(I18nUtil.getMessage("error.username.exists")));
|
||||
}
|
||||
|
||||
// 校验手机号码
|
||||
LambdaQueryWrapper<User> checkPhoneWrapper = new LambdaQueryWrapper<>();
|
||||
checkPhoneWrapper.eq(User::getUsername, user.getUsername());
|
||||
checkPhoneWrapper.ne(User::getId, user.getId());
|
||||
if(count(checkPhoneWrapper) > 0) {
|
||||
throw new RuntimeException(I18nUtil.getMessage(I18nUtil.getMessage("error.phone.exists")));
|
||||
}
|
||||
|
||||
// 校验邮箱
|
||||
LambdaQueryWrapper<User> checkEmailWrapper = new LambdaQueryWrapper<>();
|
||||
checkEmailWrapper.eq(User::getUsername, user.getUsername());
|
||||
checkEmailWrapper.ne(User::getId, user.getId());
|
||||
if(count(checkEmailWrapper) > 0) {
|
||||
throw new RuntimeException(I18nUtil.getMessage(I18nUtil.getMessage("error.email.exists")));
|
||||
}
|
||||
|
||||
return updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getByUsername(String username) {
|
||||
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
|
||||
@@ -184,14 +233,26 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resetPassword(ResetPassword resetPassword) {
|
||||
User user = getById(resetPassword.getUserId());
|
||||
public boolean resetPassword(ResetPasswordRequest resetPasswordRequest) {
|
||||
User user = getById(resetPasswordRequest.getUserId());
|
||||
if(user == null) {
|
||||
throw new RuntimeException(I18nUtil.getMessage("error.user.not.found"));
|
||||
}
|
||||
// 更新新密码
|
||||
String newPasswordMd5 = DigestUtils.md5DigestAsHex(resetPassword.getPassword().getBytes(StandardCharsets.UTF_8));
|
||||
String newPasswordMd5 = DigestUtils.md5DigestAsHex(resetPasswordRequest.getPassword().getBytes(StandardCharsets.UTF_8));
|
||||
user.setPassword(newPasswordMd5);
|
||||
return updateById(user);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean batchStatus(BizUserStatusRequest bizUserStatusRequest) {
|
||||
bizUserStatusRequest.getIds().forEach(id -> {
|
||||
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(User::getId, id);
|
||||
wrapper.set(User::getStatus, bizUserStatusRequest.getStatus());
|
||||
this.update(wrapper);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ body {
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
padding: 18px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
|
||||
55
src/main/resources/static/assets/js/confirmModal.js
Normal file
55
src/main/resources/static/assets/js/confirmModal.js
Normal file
@@ -0,0 +1,55 @@
|
||||
// assets/js/confirmModal.js
|
||||
(function(window) {
|
||||
// 全局确认弹窗函数
|
||||
window.showConfirmModal = function(options = {}) {
|
||||
// 默认配置
|
||||
const {
|
||||
title = '系统提示',
|
||||
content = '确定执行此操作吗?',
|
||||
onConfirm = () => {}, // 确认回调
|
||||
onCancel = () => {} // 取消回调(可选)
|
||||
} = options;
|
||||
|
||||
// 生成唯一ID(避免重复)
|
||||
const modalId = `confirm-modal-${Date.now()}`;
|
||||
|
||||
// 动态创建模态框DOM
|
||||
const modalHtml = `
|
||||
<div class="modal fade" id="${modalId}" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5">${title}</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">${content}</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="${modalId}-confirm">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 插入到页面
|
||||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||
|
||||
// 初始化并显示弹窗
|
||||
const modal = new bootstrap.Modal(document.getElementById(modalId));
|
||||
modal.show();
|
||||
|
||||
// 绑定确认按钮事件
|
||||
document.getElementById(`${modalId}-confirm`).addEventListener('click', () => {
|
||||
onConfirm(); // 执行确认逻辑
|
||||
modal.hide();
|
||||
});
|
||||
|
||||
// 弹窗关闭后清理DOM
|
||||
const modalElement = document.getElementById(modalId);
|
||||
modalElement.addEventListener('hidden.bs.modal', () => {
|
||||
onCancel(); // 执行取消逻辑(可选)
|
||||
modalElement.remove(); // 移除DOM,避免冗余
|
||||
});
|
||||
};
|
||||
})(window);
|
||||
145
src/main/resources/static/assets/js/message.js
Normal file
145
src/main/resources/static/assets/js/message.js
Normal file
@@ -0,0 +1,145 @@
|
||||
if (!window.Vue) {
|
||||
const vueScript = document.createElement('script');
|
||||
vueScript.src = 'https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js';
|
||||
document.head.appendChild(vueScript);
|
||||
}
|
||||
|
||||
if (!document.querySelector('link[href*="bootstrap-icons"]')) {
|
||||
const iconLink = document.createElement('link');
|
||||
iconLink.rel = 'stylesheet';
|
||||
iconLink.href = 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css';
|
||||
document.head.appendChild(iconLink);
|
||||
}
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.h5-message-container {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.h5-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 14px 24px;
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
|
||||
animation: h5MsgShow 0.4s ease-out forwards;
|
||||
}
|
||||
.h5-message.success { background-color: rgba(40, 167, 69, 0.95); }
|
||||
.h5-message.error { background-color: rgba(220, 53, 69, 0.95); }
|
||||
.h5-message.warning { background-color: rgba(255, 193, 7, 0.95); color: #333; }
|
||||
.h5-message.info { background-color: rgba(13, 110, 253, 0.95); }
|
||||
.h5-message-icon {
|
||||
margin-right: 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
@keyframes h5MsgShow {
|
||||
from { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
|
||||
to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
|
||||
}
|
||||
.h5-message.hide { animation: h5MsgHide 0.4s ease-in forwards; }
|
||||
@keyframes h5MsgHide {
|
||||
from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
|
||||
to { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
const messageTemplate = `
|
||||
<div class="h5-message" :class="[type, { hide: !visible }]">
|
||||
<i class="h5-message-icon" :class="iconClass"></i>
|
||||
<span>{{ message }}</span>
|
||||
</div>
|
||||
`;
|
||||
const messageInstances = [];
|
||||
function showMessage(options) {
|
||||
if (typeof options === 'string') {
|
||||
options = { message: options };
|
||||
}
|
||||
|
||||
const config = {
|
||||
message: '',
|
||||
type: 'info',
|
||||
duration: 3000,
|
||||
...options,
|
||||
duration: Math.max(1000, options.duration || 3000)
|
||||
};
|
||||
|
||||
const checkVueLoaded = setInterval(() => {
|
||||
if (window.Vue) {
|
||||
clearInterval(checkVueLoaded);
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.className = 'h5-message-container';
|
||||
document.body.appendChild(container);
|
||||
const app = Vue.createApp({
|
||||
template: messageTemplate,
|
||||
data() {
|
||||
return {
|
||||
message: config.message,
|
||||
type: config.type,
|
||||
visible: true
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
iconClass() {
|
||||
const icons = {
|
||||
success: 'bi bi-check-circle',
|
||||
error: 'bi bi-exclamation-circle',
|
||||
warning: 'bi bi-exclamation-triangle',
|
||||
info: 'bi bi-info-circle'
|
||||
};
|
||||
return icons[this.type] || icons.info;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const showTimer = setTimeout(() => {
|
||||
this.visible = false;
|
||||
const hideTimer = setTimeout(() => {
|
||||
|
||||
const index = messageInstances.indexOf(app);
|
||||
if (index > -1) {
|
||||
messageInstances.splice(index, 1);
|
||||
}
|
||||
app.unmount(container);
|
||||
document.body.removeChild(container);
|
||||
clearTimeout(hideTimer);
|
||||
}, 400);
|
||||
|
||||
clearTimeout(showTimer);
|
||||
}, config.duration);
|
||||
}
|
||||
});
|
||||
const instance = app.mount(container);
|
||||
messageInstances.push(instance);
|
||||
}
|
||||
}, 50);
|
||||
|
||||
setTimeout(() => {
|
||||
clearInterval(checkVueLoaded);
|
||||
if (!window.Vue) {
|
||||
alert(config.message);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
window.$message = showMessage;
|
||||
|
||||
['success', 'error', 'warning', 'info'].forEach(type => {
|
||||
window.$message[type] = (message, duration) => {
|
||||
window.$message({
|
||||
message,
|
||||
type,
|
||||
// 确保时长有效(用户未传则用默认3秒)
|
||||
duration: duration ? Math.max(1000, duration) : 500
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>用户管理系统</title>
|
||||
<title>用户管理</title>
|
||||
<!-- 外部引入库文件 -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
||||
@@ -14,7 +14,7 @@
|
||||
<div class="main-container">
|
||||
<div class="table-container">
|
||||
<!-- 页面标题 -->
|
||||
<h3 class="mb-4">用户管理</h3>
|
||||
<h4 class="mb-3">用户管理</h4>
|
||||
|
||||
<!-- 多参数搜索栏 -->
|
||||
<div class="search-bar">
|
||||
@@ -79,7 +79,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 新增按钮 -->
|
||||
<div class="mb-3">
|
||||
<div class="mb-2">
|
||||
<button class="btn btn-info" @click="openAddModal()">
|
||||
<i class="bi bi-plus-circle me-1"></i> 新增
|
||||
</button>
|
||||
@@ -99,9 +99,10 @@
|
||||
@change="toggleSelectAll()"
|
||||
>
|
||||
</th>
|
||||
<th>ID</th>
|
||||
<th>昵称</th>
|
||||
<th>用户名</th>
|
||||
<th>号码</th>
|
||||
<th>邮箱</th>
|
||||
<th>状态</th>
|
||||
<th>创建时间</th>
|
||||
<th style="width: 120px;">操作</th>
|
||||
@@ -143,9 +144,10 @@
|
||||
@change="toggleSelectItem(item.id)"
|
||||
>
|
||||
</td>
|
||||
<td>{{ item.id }}</td>
|
||||
<td>{{ item.nickName }}</td>
|
||||
<td>{{ item.username }}</td>
|
||||
<td>{{ item.telephone }}</td>
|
||||
<td>{{ item.email }}</td>
|
||||
<td>
|
||||
<span class="badge" :class="item.status === 1 ? 'bg-success' : 'bg-danger'">
|
||||
{{ item.status === 1 ? '启用' : '禁用' }}
|
||||
@@ -212,7 +214,8 @@
|
||||
<li class="page-item" :class="{ disabled: pageNum === 1 }">
|
||||
<a class="page-link" href="#" @click.prevent="changePage(pageNum - 1)">上一页</a>
|
||||
</li>
|
||||
<li class="page-item active" v-for="page in pageList" :key="page">
|
||||
<li class="page-item" :class="page === pageNum ? 'active' : ''" v-for="page in pageList"
|
||||
:key="page">
|
||||
<a class="page-link" href="#" @click.prevent="changePage(page)">{{ page }}</a>
|
||||
</li>
|
||||
<li class="page-item" :class="{ disabled: pageNum === totalPages }">
|
||||
@@ -240,27 +243,34 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label for="recipient-name" class="col-form-label">昵称:</label>
|
||||
<input type="text" class="form-control" disabled :value="resetPasswordDto.nickName">
|
||||
<div class="mb-3 row">
|
||||
<label class="col-sm-2 col-form-label">昵称</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" v-model="resetPasswordDto.nickName" disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="recipient-name" class="col-form-label">用户名:</label>
|
||||
<input type="text" class="form-control" disabled :value="resetPasswordDto.username">
|
||||
</div>
|
||||
|
||||
<div class="mb-3 ">
|
||||
<label for="togglePassword" class="form-label">密码</label>
|
||||
<div class="position-relative">
|
||||
<div class="mb-3 row">
|
||||
<label class="col-sm-2 col-form-label">用户名</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" v-model="resetPasswordDto.username" disabled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 row">
|
||||
<label class="col-sm-2 col-form-label">密码</label>
|
||||
<div class="col-sm-10 position-relative">
|
||||
<!-- 密码输入框 -->
|
||||
<input :type="isPasswordVisible ? 'text' : 'password'" class="form-control pe-10" id="togglePassword"
|
||||
<input :type="isPasswordVisible ? 'text' : 'password'" class="form-control pe-10"
|
||||
id="togglePassword"
|
||||
placeholder="请输入密码" v-model="resetPasswordDto.password">
|
||||
<!-- 切换按钮(绝对定位在输入框右侧) -->
|
||||
<button type="button"
|
||||
@click="togglePasswordVisibility"
|
||||
class="btn btn-transparent position-absolute end-0 top-0 h-100 px-3 border-0 bg-transparent"
|
||||
id="toggleBtn">
|
||||
<i :class="isPasswordVisible ? 'bi bi-eye-slash text-secondary' : 'bi bi-eye text-secondary'" id="toggleIcon"></i>
|
||||
<i :class="isPasswordVisible ? 'bi bi-eye-slash text-secondary' : 'bi bi-eye text-secondary'"
|
||||
id="toggleIcon"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -275,95 +285,88 @@
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 新增 -->
|
||||
<div class="modal fade" id="addModal" tabindex="-1" aria-labelledby="addModalLabel" aria-hidden="true">
|
||||
<!-- 新增 or 编辑 -->
|
||||
<div class="modal fade" id="addOrEditModel" tabindex="-1" aria-labelledby="addOrEditModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="addModalLabel">New message</h1>
|
||||
<h1 class="modal-title fs-5" id="addOrEditModalLabel">{{ addOrEditTitle }}</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label for="recipient-name" class="col-form-label">Recipient:</label>
|
||||
<input type="text" class="form-control">
|
||||
<div class="mb-3 row">
|
||||
<label class="col-sm-2 col-form-label">昵称</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" v-model="addOrEditDto.nickName"
|
||||
placeholder="请输入昵称">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label class="col-sm-2 col-form-label">用户名</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" v-model="addOrEditDto.username"
|
||||
placeholder="请输入用户名">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row" v-if="addOrEditDto.id == null">
|
||||
<label class="col-sm-2 col-form-label">密码</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" v-model="addOrEditDto.password"
|
||||
placeholder="请输入密码">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label class="col-sm-2 col-form-label">号码</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" v-model="addOrEditDto.telephone"
|
||||
placeholder="请输入号码">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label class="col-sm-2 col-form-label">邮箱</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" v-model="addOrEditDto.email"
|
||||
placeholder="请输入邮箱">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="message-text" class="col-form-label">Message:</label>
|
||||
<textarea class="form-control"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary">Send message</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
||||
<button type="button" class="btn btn-primary" @click.prevent="saveUser">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 修改 -->
|
||||
<div class="modal fade" id="editModel" tabindex="-1" aria-labelledby="editModelModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="editModelModalLabel">New message</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label for="recipient-name" class="col-form-label">Recipient:</label>
|
||||
<input type="text" class="form-control" id="recipient-name">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="message-text" class="col-form-label">Message:</label>
|
||||
<textarea class="form-control" id="message-text"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary">Send message</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 外部引入库文件 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios@1.4.0/dist/axios.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- 外部引入模拟请求文件 -->
|
||||
<script src="/assets/js/axiosRequest.js"></script>
|
||||
<script src="/assets/js/message.js"></script>
|
||||
<script src="/assets/js/confirmModal.js"></script>
|
||||
|
||||
<!-- 页面核心逻辑 -->
|
||||
<script>
|
||||
const {createApp} = Vue;
|
||||
|
||||
createApp({
|
||||
data() {
|
||||
return {
|
||||
tableData: [], // 表格数据源
|
||||
loading: false, // 加载状态
|
||||
// 多搜索参数绑定(与搜索栏对应)
|
||||
tableData: [],
|
||||
loading: false,
|
||||
searchParams: {
|
||||
nickName: '',
|
||||
username: '',
|
||||
status: '',
|
||||
},
|
||||
// 分页参数
|
||||
pageNum: 1, // 当前页码
|
||||
pageSize: 10, // 每页条数
|
||||
total: 0, // 总数据量
|
||||
totalPages: 0, // 总页数
|
||||
// 批量操作
|
||||
selectedIds: [], // 选中的ID集合
|
||||
selectAll: false, // 全选状态
|
||||
batchAction: '', // 批量操作选择
|
||||
// 分页显示范围(最多显示5个页码)
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
totalPages: 0,
|
||||
selectedIds: [],
|
||||
selectAll: false,
|
||||
batchAction: '',
|
||||
pageRange: 5,
|
||||
modalInstances: {},
|
||||
resetPasswordDto: {
|
||||
@@ -372,7 +375,17 @@
|
||||
username: null,
|
||||
password: null,
|
||||
},
|
||||
isPasswordVisible: false
|
||||
isPasswordVisible: false,
|
||||
addOrEditTitle: '',
|
||||
addOrEditDto: {
|
||||
id: null,
|
||||
username: null,
|
||||
nickName: '',
|
||||
password: null,
|
||||
status: 1,
|
||||
email: '',
|
||||
telephone: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -380,18 +393,13 @@
|
||||
pageList() {
|
||||
const list = [];
|
||||
if (this.totalPages === 0) return list;
|
||||
|
||||
// 总页数小于等于显示范围,直接显示所有页码
|
||||
if (this.totalPages <= this.pageRange) {
|
||||
for (let i = 1; i <= this.totalPages; i++) {
|
||||
list.push(i);
|
||||
}
|
||||
} else {
|
||||
// 总页数大于显示范围,显示当前页前后2个页码
|
||||
let start = Math.max(1, this.pageNum - 2);
|
||||
let end = Math.min(this.totalPages, this.pageNum + 2);
|
||||
|
||||
// 确保显示5个页码
|
||||
if (end - start < this.pageRange - 1) {
|
||||
if (start === 1) {
|
||||
end = this.pageRange;
|
||||
@@ -408,7 +416,6 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 格式化时间(毫秒转字符串)
|
||||
formatTime(time) {
|
||||
if (!time) return '-';
|
||||
const date = new Date(time);
|
||||
@@ -422,22 +429,20 @@
|
||||
});
|
||||
},
|
||||
|
||||
// 获取表格数据(核心方法)
|
||||
async fetchData() {
|
||||
this.loading = true;
|
||||
try {
|
||||
// 调用模拟接口获取数据
|
||||
const response = await request.get('/biz/user/page', {
|
||||
...this.searchParams, // 传递所有搜索参数
|
||||
...this.searchParams,
|
||||
current: this.pageNum,
|
||||
size: this.pageSize
|
||||
});
|
||||
console.log(response)
|
||||
|
||||
if (response.code === 200) {
|
||||
this.tableData = response.data.records; // 列表数据
|
||||
this.total = response.data.total; // 总条数
|
||||
this.totalPages = response.data.pages; // 总页数
|
||||
this.tableData = response.data.records;
|
||||
this.total = response.data.total;
|
||||
this.totalPages = response.data.pages;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error);
|
||||
@@ -446,7 +451,7 @@
|
||||
this.totalPages = 0;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
this.clearSelected(); // 每次加载数据清空选中状态
|
||||
this.clearSelected();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -455,28 +460,25 @@
|
||||
// 边界判断
|
||||
if (page < 1 || page > this.totalPages || page === this.pageNum) return;
|
||||
this.pageNum = page;
|
||||
this.fetchData(); // 切换页码后重新加载数据
|
||||
this.fetchData();
|
||||
},
|
||||
|
||||
// 重置搜索
|
||||
resetSearch() {
|
||||
// 重置所有搜索参数
|
||||
this.searchParams = {
|
||||
nickName: '',
|
||||
username: '',
|
||||
status: '',
|
||||
};
|
||||
this.pageNum = 1; // 重置到第一页
|
||||
this.pageNum = 1;
|
||||
this.fetchData();
|
||||
},
|
||||
|
||||
// 全选/取消全选
|
||||
toggleSelectAll() {
|
||||
if (this.selectAll) {
|
||||
// 全选:收集所有数据的ID
|
||||
this.selectedIds = this.tableData.map(item => item.id);
|
||||
} else {
|
||||
// 取消全选:清空选中ID
|
||||
this.selectedIds = [];
|
||||
}
|
||||
},
|
||||
@@ -516,55 +518,65 @@
|
||||
|
||||
// 批量删除
|
||||
async handleBatchDelete() {
|
||||
if (!confirm(`确定要删除选中的 ${this.selectedIds.length} 条数据吗?`)) return;
|
||||
|
||||
showConfirmModal({
|
||||
title: '删除确认',
|
||||
content: `确定要删除选中的 ${this.selectedIds.length} 条数据吗?`,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
const response = await request.post('/sys/data/batchDelete', {
|
||||
ids: this.selectedIds
|
||||
});
|
||||
|
||||
const response = await request.post('/biz/user/batchDelete', {ids: this.selectedIds});
|
||||
if (response.code === 200) {
|
||||
alert('删除成功!');
|
||||
this.fetchData(); // 重新加载数据
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('批量删除失败:', error);
|
||||
alert('删除失败,请重试!');
|
||||
}
|
||||
},
|
||||
|
||||
// 批量修改状态(启用/禁用)
|
||||
async handleBatchStatus(status) {
|
||||
try {
|
||||
const response = await request.post('/sys/data/batchUpdateStatus', {
|
||||
ids: this.selectedIds,
|
||||
status: status
|
||||
});
|
||||
|
||||
if (response.code === 200) {
|
||||
alert(`已${status === 1 ? '启用' : '禁用'}选中数据!`);
|
||||
this.fetchData(); // 重新加载数据
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('批量更新状态失败:', error);
|
||||
alert('操作失败,请重试!');
|
||||
}
|
||||
},
|
||||
|
||||
// 单个删除
|
||||
async handleDelete(id) {
|
||||
if (!confirm('确定要删除这条数据吗?')) return;
|
||||
|
||||
try {
|
||||
const response = await request.post('/sys/data/delete', {id});
|
||||
if (response.code === 200) {
|
||||
alert('删除成功!');
|
||||
$message.success("删除成功");
|
||||
this.fetchData(); // 重新加载数据
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
alert('删除失败,请重试!');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 批量修改状态(启用/禁用)
|
||||
async handleBatchStatus(status) {
|
||||
showConfirmModal({
|
||||
title: '提示',
|
||||
content: '确认更新这些数据吗?',
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
const response = await request.post('/biz/user/batchStatus', {
|
||||
ids: this.selectedIds,
|
||||
status: status
|
||||
});
|
||||
if (response.code === 200) {
|
||||
$message.success(`已${status === 1 ? '启用' : '禁用'}选中数据!`);
|
||||
this.fetchData();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('批量更新状态失败:', error);
|
||||
$message.error('操作失败,请重试!');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 单个删除
|
||||
async handleDelete(id) {
|
||||
showConfirmModal({
|
||||
title: '删除确认',
|
||||
content: '确定要删除这条数据吗?',
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
const response = await request.post('/biz/user/delete', {id});
|
||||
if (response.code === 200) {
|
||||
$message.success("删除成功");
|
||||
this.fetchData(); // 重新加载数据
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
$message.error('删除失败,请重试!');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 打开重置密码墨台框
|
||||
openPwdModal(item) {
|
||||
@@ -572,51 +584,82 @@
|
||||
this.resetPasswordDto.userId = item.id;
|
||||
this.resetPasswordDto.username = item.username;
|
||||
this.resetPasswordDto.nickName = item.nickName;
|
||||
this.resetPasswordDto.password = '';
|
||||
this.modalInstances['resetPwdModal'].show();
|
||||
},
|
||||
resetPwd() {
|
||||
request.put("/biz/user/resetPassword", this.resetPasswordDto)
|
||||
.then((result) => {
|
||||
console.log(result)
|
||||
if (result.code === 200) {
|
||||
alert('修改成功!');
|
||||
request.put("/biz/user/resetPasswordRequest", this.resetPasswordDto)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
$message.success('修改成功!', 500);
|
||||
this.modalInstances['resetPwdModal'].hide();
|
||||
} else {
|
||||
alert('修改失败!');
|
||||
$message.error('修改失败,请重试!');
|
||||
}
|
||||
this.resetPasswordDto = {};
|
||||
});
|
||||
}).catch((error) => {
|
||||
$message.error('修改失败,请重试!');
|
||||
})
|
||||
|
||||
},
|
||||
// 打开新增模态框
|
||||
openAddModal() {
|
||||
alert('打开新增模态框');
|
||||
// 实际项目中添加模态框显示逻辑
|
||||
this.addOrEditTitle = '新增';
|
||||
this.clearForm();
|
||||
this.modalInstances['addOrEditModel'].show();
|
||||
},
|
||||
|
||||
// 打开编辑模态框
|
||||
openEditModal(item) {
|
||||
alert(`打开编辑模态框,编辑ID: ${item.id}`);
|
||||
// 实际项目中添加模态框显示和数据回显逻辑
|
||||
this.clearForm();
|
||||
this.addOrEditTitle = '编辑';
|
||||
this.addOrEditDto = item;
|
||||
this.modalInstances['addOrEditModel'].show();
|
||||
},
|
||||
saveUser() {
|
||||
let url = (this.addOrEditDto === null || this.addOrEditDto.id === null) ? '/biz/user/save' : '/biz/user/update';
|
||||
request.post(url, this.addOrEditDto)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
$message.success('保存成功!', 500);
|
||||
this.fetchData();
|
||||
this.modalInstances['addOrEditModel'].hide();
|
||||
} else {
|
||||
$message.error('保存失败!', 500);
|
||||
}
|
||||
this.clearForm();
|
||||
|
||||
}).catch(() => {
|
||||
|
||||
})
|
||||
},
|
||||
// 切换密码可见性的方法
|
||||
togglePasswordVisibility() {
|
||||
this.isPasswordVisible = !this.isPasswordVisible; // 反转状态
|
||||
this.isPasswordVisible = !this.isPasswordVisible;
|
||||
},
|
||||
// 清空表单数据
|
||||
clearForm() {
|
||||
this.addOrEditDto = {
|
||||
id: null,
|
||||
username: null,
|
||||
nickName: '',
|
||||
password: null,
|
||||
status: 1,
|
||||
email: '',
|
||||
telephone: ''
|
||||
};
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
// 页面加载完成后初始化数据
|
||||
this.fetchData();
|
||||
const modalIds = ['resetPwdModal', 'editModel', 'addModel']; // 所有模态框的ID集合
|
||||
const modalIds = ['resetPwdModal', 'addOrEditModel'];
|
||||
modalIds.forEach(id => {
|
||||
console.log(id)
|
||||
const modalElement = document.getElementById(id);
|
||||
// 先检查元素是否存在,避免报错
|
||||
if (modalElement) {
|
||||
this.modalInstances[id] = new bootstrap.Modal(modalElement, {
|
||||
backdrop: 'static', // 统一配置,也可单独设置
|
||||
keyboard: true // 按 ESC 不关闭(可选)
|
||||
backdrop: 'static',
|
||||
keyboard: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user