<template>
<div class="user-list">
<div class="search-bar">
<el-form :inline="true" :model="searchForm" class="demo-form-inline">
<el-form-item label="用户名">
<el-input v-model="searchForm.username" placeholder="请输入用户名" clearable />
</el-form-item>
<el-form-item label="手机号">
<el-input v-model="searchForm.phone" placeholder="请输入手机号" clearable />
</el-form-item>
<el-form-item label="用户类型">
<el-select v-model="searchForm.userType" placeholder="请选择" clearable>
<el-option label="普通用户" value="normal" />
<el-option label="VIP用户" value="vip" />
<el-option label="企业用户" value="enterprise" />
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="searchForm.status" placeholder="请选择" clearable>
<el-option label="正常" value="normal" />
<el-option label="禁用" value="disabled" />
<el-option label="待审核" value="pending" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="operation-bar">
<el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>新增用户
</el-button>
<el-button type="danger" @click="handleBatchDelete" :disabled="!selectedUsers.length">
<el-icon><Delete /></el-icon>批量删除
</el-button>
<el-button type="success" @click="handleExport">
<el-icon><Download /></el-icon>导出数据
</el-button>
</div>
<el-table
v-loading="loading"
:data="userList"
@selection-change="handleSelectionChange"
border
style="width: 100%"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="username" label="用户名" />
<el-table-column prop="realName" label="真实姓名" />
<el-table-column prop="phone" label="手机号" />
<el-table-column prop="userType" label="用户类型">
<template #default="scope">
<el-tag :type="getUserTypeTag(scope.row.userType)">
{{ getUserTypeLabel(scope.row.userType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-tag :type="getStatusTag(scope.row.status)">
{{ getStatusLabel(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" />
<el-table-column label="操作" width="250">
<template #default="scope">
<el-button type="primary" link @click="handleEdit(scope.row)">
编辑
</el-button>
<el-button type="success" link @click="handleViewFamily(scope.row)">
家庭关系
</el-button>
<el-button
type="danger"
link
@click="handleDelete(scope.row)"
v-if="scope.row.status !== 'disabled'"
>
禁用
</el-button>
<el-button
type="success"
link
@click="handleEnable(scope.row)"
v-else
>
启用
</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<!-- 用户编辑对话框 -->
<el-dialog
v-model="dialogVisible"
:title="dialogType === 'add' ? '新增用户' : '编辑用户'"
width="500px"
>
<el-form
ref="userFormRef"
:model="userForm"
:rules="userRules"
label-width="100px"
>
<el-form-item label="用户名" prop="username">
<el-input v-model="userForm.username" :disabled="dialogType === 'edit'" />
</el-form-item>
<el-form-item label="真实姓名" prop="realName">
<el-input v-model="userForm.realName" />
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="userForm.phone" />
</el-form-item>
<el-form-item label="用户类型" prop="userType">
<el-select v-model="userForm.userType" style="width: 100%">
<el-option label="普通用户" value="normal" />
<el-option label="VIP用户" value="vip" />
<el-option label="企业用户" value="enterprise" />
</el-select>
</el-form-item>
<el-form-item label="密码" prop="password" v-if="dialogType === 'add'">
<el-input v-model="userForm.password" type="password" show-password />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { Plus, Delete, Download } from '@element-plus/icons-vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import {
searchUsers,
addUser,
updateUser,
deleteUser,
batchDeleteUsers,
enableUser,
disableUser
} from '@/api/user';
// 搜索表单
const searchForm = reactive({
username: '',
phone: '',
userType: '',
status: ''
});
// 表格数据
const loading = ref(false);
const userList = ref([]);
const selectedUsers = ref([]);
// 分页
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
// 对话框
const dialogVisible = ref(false);
const dialogType = ref('add'); // 'add' 或 'edit'
const userFormRef = ref(null);
const userForm = reactive({
username: '',
realName: '',
phone: '',
userType: 'normal',
password: ''
});
// 表单验证规则
const userRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
realName: [
{ required: true, message: '请输入真实姓名', trigger: 'blur' }
],
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
],
userType: [
{ required: true, message: '请选择用户类型', trigger: 'change' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
]
};
// 用户类型标签
const getUserTypeTag = (type) => {
const map = {
normal: '',
vip: 'success',
enterprise: 'warning'
};
return map[type];
};
const getUserTypeLabel = (type) => {
const map = {
normal: '普通用户',
vip: 'VIP用户',
enterprise: '企业用户'
};
return map[type];
};
// 状态标签
const getStatusTag = (status) => {
const map = {
normal: 'success',
disabled: 'danger',
pending: 'warning'
};
return map[status];
};
const getStatusLabel = (status) => {
const map = {
normal: '正常',
disabled: '禁用',
pending: '待审核'
};
return map[status];
};
// 搜索
const handleSearch = async () => {
loading.value = true;
try {
const response = await searchUsers({
...searchForm,
page: currentPage.value,
pageSize: pageSize.value
});
userList.value = response.data.data.records;
total.value = response.data.data.total;
} catch (error) {
ElMessage.error('搜索失败,请稍后重试');
} finally {
loading.value = false;
}
};
const resetSearch = () => {
Object.keys(searchForm).forEach(key => {
searchForm[key] = '';
});
handleSearch();
};
// 表格选择
const handleSelectionChange = (val) => {
selectedUsers.value = val;
};
// 分页
const handleSizeChange = (val) => {
pageSize.value = val;
handleSearch();
};
const handleCurrentChange = (val) => {
currentPage.value = val;
handleSearch();
};
// 新增用户
const handleAdd = () => {
dialogType.value = 'add';
dialogVisible.value = true;
Object.keys(userForm).forEach(key => {
userForm[key] = '';
});
userForm.userType = 'normal';
};
// 编辑用户
const handleEdit = (row) => {
dialogType.value = 'edit';
dialogVisible.value = true;
Object.assign(userForm, row);
};
// 提交表单
// 提交表单
const handleSubmit = () => {
if (!userFormRef.value) return;
userFormRef.value.validate(async (valid, fields) => { // 修改此处为 async 函数
if (valid) {
try {
if (dialogType.value === 'add') {
await addUser(userForm); // 现在可以正常使用 await
} else {
await updateUser(userForm);
}
dialogVisible.value = false;
ElMessage.success('操作成功');
handleSearch();
} catch (error) {
ElMessage.error('操作失败,请稍后重试');
}
}
});
};
// 删除用户
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm('确定要禁用该用户吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
await disableUser(row.id);
ElMessage.success('禁用成功');
handleSearch();
} catch (error) {
if (error.name === 'CancelError') {
// 用户取消操作
} else {
ElMessage.error('禁用失败,请稍后重试');
}
}
};
// 启用用户
const handleEnable = async (row) => {
try {
await ElMessageBox.confirm('确定要启用该用户吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
await enableUser(row.id);
ElMessage.success('启用成功');
handleSearch();
} catch (error) {
if (error.name === 'CancelError') {
// 用户取消操作
} else {
ElMessage.error('启用失败,请稍后重试');
}
}
};
// 批量删除用户
const handleBatchDelete = async () => {
try {
await ElMessageBox.confirm('确定要批量删除选中的用户吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
const ids = selectedUsers.value.map(user => user.id);
await batchDeleteUsers(ids);
ElMessage.success('批量删除成功');
handleSearch();
} catch (error) {
if (error.name === 'CancelError') {
// 用户取消操作
} else {
ElMessage.error('批量删除失败,请稍后重试');
}
}
};
// 导出数据
const handleExport = () => {
// 这里可以实现导出数据的逻辑,例如生成Excel文件等
ElMessage.warning('导出数据功能待实现');
};
// 查看家庭关系
const handleViewFamily = (row) => {
// 这里可以实现查看家庭关系的逻辑,例如跳转到家庭关系页面等
ElMessage.warning('查看家庭关系功能待实现');
};
</script>
<style scoped>
.user-list {
padding: 20px;
background: #fff;
border-radius: 4px;
}
.search-bar {
margin-bottom: 20px;
padding: 20px;
background: #f5f7fa;
border-radius: 4px;
}
.operation-bar {
margin-bottom: 20px;
display: flex;
gap: 10px;
}
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
:deep(.el-tag) {
margin-right: 5px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
</style> import axios from 'axios'
const baseURL = 'http://localhost:8080/api/home_health/user'
const instance = axios.create({
baseURL,
timeout: 5000
})
// 搜索用户
// 搜索用户
export const searchUsers = (params) => {
return instance.get('/page', { params })
.then(response => {
switch (response.status) {
case 200:
return response.data;
case 400:
throw new Error('Bad Request');
case 401:
throw new Error('Unauthorized');
case 404:
throw new Error('Not Found');
default:
throw new Error(`Request failed with status code ${response.status}`);
}
})
.catch(error => {
if (axios.isAxiosError(error)) {
if (error.response) {
console.error('Search users error:', error.response.data);
} else if (error.request) {
console.error('Search users error: No response received', error.request);
} else {
console.error('Search users error:', error.message);
}
} else {
console.error('Search users error:', error);
}
throw error;
});
};
// 新增用户
export const addUser = (user) => {
return instance.post('/add', user);
};
// 编辑用户
export const updateUser = (user) => {
return instance.put('/update', user);
};
// 删除用户
export const deleteUser = (id) => {
return instance.delete(`/delete/${id}`);
};
// 批量删除用户
export const batchDeleteUsers = (ids) => {
return instance.delete('/batchDelete', { data: ids });
};
// 启用用户
export const enableUser = (id) => {
// 假设这里需要更新用户状态为正常,具体根据后端逻辑调整
return instance.put('/enable', { id, status: 1 });
};
// 禁用用户
export const disableUser = (id) => {
// 假设这里需要更新用户状态为禁用,具体根据后端逻辑调整
return instance.put('/disable', { id, status: 0 });
}; import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
export default defineConfig({
plugins: [vue(), vueDevTools()], // 添加 vueDevTools 插件
server: {
proxy: {
'/api': {
target: 'http://localhost:8080/api/home_health',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
}) package com.itheima.springboot.pojo.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@Data
@TableName("user")
public class User implements Serializable {
@TableId
private Long id;
private String username; // 登录账户名
private String realName; // 真实姓名
private String phone; // 手机号
@JsonIgnore // 避免密码泄露
private String password;
@JsonIgnore // 避免盐值泄露
private String salt; // 密码盐值
private String userType; // 用户类型(normal/vip/enterprise)
private String status; // 用户状态(normal/disabled/pending)
private Integer role; // 权限级别
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date lastLoginTime;
private String lastLoginIp;
private String lastLoginDevice;
private List<Family> families; // 家庭成员列表
// Getters and Setters
// (保持原有内容不变)
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getRole() {
return role;
}
public void setRole(Integer role) {
this.role = role;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getLastLoginTime() {
return lastLoginTime;
}
public void setLastLoginTime(Date lastLoginTime) {
this.lastLoginTime = lastLoginTime;
}
public String getLastLoginIp() {
return lastLoginIp;
}
public void setLastLoginIp(String lastLoginIp) {
this.lastLoginIp = lastLoginIp;
}
public String getLastLoginDevice() {
return lastLoginDevice;
}
public void setLastLoginDevice(String lastLoginDevice) {
this.lastLoginDevice = lastLoginDevice;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public List<Family> getFamilies() {
return families;
}
public void setFamilies(List<Family> families) {
this.families = families;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getUserType() {
return userType;
}
public void setUserType(String userType) {
this.userType = userType;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
} package com.itheima.springboot.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.springboot.pojo.api.ApiResult;
import com.itheima.springboot.pojo.dto.LoginRequest;
import com.itheima.springboot.pojo.dto.LoginResponse;
import com.itheima.springboot.pojo.entity.User;
import com.itheima.springboot.service.UserService;
import com.itheima.springboot.utils.ResultUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@Autowired
private UserService userService;
@PostMapping("/login")
public ResultUtil<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
try {
LoginResponse response = userService.login(request);
return ResultUtil.success("登录成功", response);
} catch (Exception e) {
logger.error("登录失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
// 新增用户
@PostMapping("/add")
public ApiResult<User> addUser(@RequestBody User user) {
try {
boolean success = userService.save(user);
if (success) {
return ApiResult.success("新增成功", user);
} else {
return ApiResult.error("新增失败");
}
} catch (Exception e) {
logger.error("新增用户失败: {}", e.getMessage(), e);
return ApiResult.error("新增用户时发生异常: " + e.getMessage());
}
}
// 删除用户
@DeleteMapping("/delete/{id}")
public ApiResult<String> deleteUser(@PathVariable Long id) {
try {
boolean success = userService.removeById(id);
if (success) {
return ApiResult.success("删除成功");
} else {
return ApiResult.error("删除失败");
}
} catch (Exception e) {
logger.error("删除用户失败: {}", e.getMessage(), e);
return ApiResult.error("删除用户时发生异常: " + e.getMessage());
}
}
// 修改用户信息
@PutMapping("/update")
public ApiResult<User> updateUser(@RequestBody User user) {
try {
boolean success = userService.updateById(user);
if (success) {
return ApiResult.success("修改成功", user);
} else {
return ApiResult.error("修改失败");
}
} catch (Exception e) {
logger.error("修改用户信息失败: {}", e.getMessage(), e);
return ApiResult.error("修改用户信息时发生异常: " + e.getMessage());
}
}
// 查询单个用户信息
@GetMapping("/get/{id}")
public ApiResult<User> getUser(@PathVariable Long id) {
try {
User user = userService.getById(id);
if (user != null) {
return ApiResult.success("查询成功", user);
} else {
return ApiResult.error("未找到该用户信息");
}
} catch (Exception e) {
logger.error("查询用户信息失败: {}", e.getMessage(), e);
return ApiResult.error("查询用户信息时发生异常: " + e.getMessage());
}
}
// 查询所有用户信息
@GetMapping("/list")
public ApiResult<List<User>> listUsers() {
try {
List<User> users = userService.list();
return ApiResult.success("查询成功", users);
} catch (Exception e) {
logger.error("查询所有用户信息失败: {}", e.getMessage(), e);
return ApiResult.error("查询所有用户信息时发生异常: " + e.getMessage());
}
}
// 分页查询用户信息
@GetMapping("/page")
public ApiResult<IPage<User>> pageUsers(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
try {
Page<User> page = new Page<>(pageNum, pageSize);
IPage<User> userPage = userService.page(page, new QueryWrapper<>());
return ApiResult.success("查询成功", userPage);
} catch (Exception e) {
logger.error("分页查询用户信息失败: {}", e.getMessage(), e);
return ApiResult.error("分页查询用户信息时发生异常: " + e.getMessage());
}
}
@DeleteMapping("/batchDelete")
public ApiResult<String> batchDeleteUsers(@RequestBody List<Long> ids) {
try {
boolean success = userService.removeByIds(ids);
if (success) {
return ApiResult.success("批量删除成功");
} else {
return ApiResult.error("批量删除失败");
}
} catch (Exception e) {
logger.error("批量删除用户失败: {}", e.getMessage(), e);
return ApiResult.error("批量删除用户时发生异常: " + e.getMessage());
}
}
// 启用用户
@PutMapping("/enable")
public ApiResult<String> enableUser(@RequestBody Map<String, Object> params) {
Long id = Long.valueOf(params.get("id").toString());
String status = String.valueOf(Integer.valueOf(params.get("status").toString()));
try {
User user = userService.getById(id);
user.setStatus(status);
boolean success = userService.updateById(user);
if (success) {
return ApiResult.success("启用成功");
} else {
return ApiResult.error("启用失败");
}
} catch (Exception e) {
logger.error("启用用户失败: {}", e.getMessage(), e);
return ApiResult.error("启用用户时发生异常: " + e.getMessage());
}
}
// 禁用用户
@PutMapping("/disable")
public ApiResult<String> disableUser(@RequestBody Map<String, Object> params) {
Long id = Long.valueOf(params.get("id").toString());
String status = String.valueOf(Integer.valueOf(params.get("status").toString()));
try {
User user = userService.getById(id);
user.setStatus(status);
boolean success = userService.updateById(user);
if (success) {
return ApiResult.success("禁用成功");
} else {
return ApiResult.error("禁用失败");
}
} catch (Exception e) {
logger.error("禁用用户失败: {}", e.getMessage(), e);
return ApiResult.error("禁用用户时发生异常: " + e.getMessage());
}
}
}server:
port: 8080
servlet:
context-path: /api/home_health
my-server:
api-context-path: /api/home_health
# MySQL与文件上传限制配置
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/home_health?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai # 添加了时区设置
username: root
password: 123456
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 开启驼峰命名转换
mapper-locations: classpath:mapper/*.xml
posture:
thresholds:
shoulder-diff: 0.1
hunchback: 0.3