这一期继续完成用户管理的增删改查和登录用户修改自己信息的功能,首先完成后端接口的功能。
1 后端接口
这边有查询列表接口、查询单个接口、新增接口、修改接口、删除接口这5个接口:
#** 用户信息的增删改查 ***
# 用户列表
@main.route('/users', methods=['GET'])
def get_users():
try:
username = request.args.get('username', '') # 获取查询参数中的 title
page = int(request.args.get('page', 1)) # 获取当前页码,默认为 1
limit = int(request.args.get('limit', 10)) # 获取每页显示的记录数,默认为 10
# 根据 title 进行模糊搜索
query = User.query.filter(User.username.like(f'%{username}%'), User.deleted==0)
# 计算总数和获取当前页数据
total = query.count() # 总记录数
records = query.offset((page - 1) * limit).limit(limit).all() # 当前页的数据
result = users_schema.dump(records) # 使用你的序列化方案处理数据
return make_response(data={'total': total, 'records': result})
except Exception as e:
return make_response(code=1, message=str(e))
@main.route('/user/<int:id>', methods=['GET'])
def get_user(id):
try:
user = User.query.get(id) # 根据 ID 查询用户
result = user_schema.dump(user) # 使用你的序列化方案处理数据
return make_response(data=result)
except Exception as e:
return make_response(code=1, message=str(e))
@main.route('/user', methods=['POST'])
def add_user():
data = request.json # 获取JSON数据
# 这里可以进行数据验证,例如检查必填字段是否存在
required_fields = ['username', 'realname', 'job', 'age', 'addr', 'intro', 'phone', 'email']
for field in required_fields:
if field not in data:
return make_response(code=1, message=f'错误,缺少字段: {field}')
notnull_fields = ['username']
for field in notnull_fields:
if data[field]=='' or data[field]==None:
return make_response(code=1, message= f'错误,字段不能为空: {field}')
hashed_password = generate_password_hash('123456')
# 创建新的用户对象
new_record = User(
realname=data['realname'],
username=data['username'],
password=hashed_password,
job=data['job'],
age=data['age'],
addr=data['addr'],
intro=data['intro'],
phone=data['phone'],
email=data['email'],
deleted=0
)
# 将新景点添加到数据库
db.session.add(new_record)
db.session.commit()
return make_response(code=0, message='添加用户成功')
@main.route('/user/<int:id>', methods=['PUT'])
def update_user(id):
data = request.json # 获取JSON数据
user = User.query.get(id) # 根据ID查找
if not user:
return make_response(code=1, message='用户不存在')
# 更新字段
for field in ['realname', 'job', 'addr', 'intro', 'phone', 'email', 'age']:
if field in data:
setattr(user, field, data[field])
db.session.commit()
return make_response(code=0, message='修改用户成功')
@main.route('/user/<int:id>', methods=['DELETE'])
def delete_user(id):
user = User.query.get(id) # 根据ID查找景点
if not user:
return make_response(code=1, message='用户不存在')
user.deleted = 1
db.session.commit()
return make_response(code=0, message='删除用户成功')
2 前端 Users.vue界面
因为前面已经做过旅游景点信息的增删改查了,所以这次加快速度,直接给出完整源码,先添加users.js的接口:
<template>
<div class="users-container">
<el-card class="box-card">
<div slot="header" class="header">
<span class="header-title">用户管理</span>
<div class="header-controls">
<el-input v-model="searchParam" placeholder="输入标题进行搜索" class="search-input"></el-input>
<el-button type="primary" @click="fetchData">搜索</el-button>
<el-button type="success" @click="handleAdd">添加用户</el-button>
</div>
</div>
<el-table :data="records" style="width: 100%">
<el-table-column prop="id" label="ID" width="50"></el-table-column>
<el-table-column prop="username" label="用户名" min-width="180"></el-table-column>
<el-table-column prop="realname" label="姓名" min-width="180"></el-table-column>
<el-table-column prop="age" label="年龄" min-width="180"></el-table-column>
<el-table-column prop="job" label="职业" min-width="180"></el-table-column>
<el-table-column prop="phone" label="电话" min-width="180"></el-table-column>
<el-table-column prop="addr" label="地址" min-width="180"></el-table-column>
<!-- <el-table-column prop="intro" label="签名" min-width="180"></el-table-column>-->
<el-table-column prop="email" label="邮箱"></el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button @click="handleEdit(scope.row)" type="text" size="small">编辑</el-button>
<el-button @click="handleDelete(scope.row)" type="text" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-size="pageSize"
:total="totalItems"
layout="total, sizes, prev, pager, next, jumper"
/>
</el-card>
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible">
<el-form :model="form">
<el-form-item label="用户名" :label-width="formLabelWidth">
<el-input v-model="form.username"></el-input>
</el-form-item>
<el-form-item label="姓名" :label-width="formLabelWidth">
<el-input v-model="form.realname"></el-input>
</el-form-item>
<!-- 年龄输入框 -->
<el-form-item label="年龄" :label-width="formLabelWidth">
<el-input-number
v-model="form.age"
:min="0"
:max="120"
size="small"
></el-input-number>
</el-form-item>
<el-form-item label="地址" :label-width="formLabelWidth">
<el-input v-model="form.addr"></el-input>
</el-form-item>
<!-- 职业选择框 -->
<el-form-item label="职业" :label-width="formLabelWidth">
<el-select v-model="form.job" placeholder="请选择职业">
<el-option label="学生" value="学生"></el-option>
<el-option label="公务员" value="公务员"></el-option>
<el-option label="律师" value="律师"></el-option>
<el-option label="IT工程师" value="IT工程师"></el-option>
<el-option label="外卖员" value="外卖员"></el-option>
<el-option label="文员" value="文员"></el-option>
</el-select>
</el-form-item>
<el-form-item label="电话" :label-width="formLabelWidth">
<el-input v-model="form.phone"></el-input>
</el-form-item>
<el-form-item label="邮箱" :label-width="formLabelWidth">
<el-input v-model="form.email"></el-input>
</el-form-item>
<el-form-item label="签名" :label-width="formLabelWidth">
<el-input v-model="form.intro"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSave">保存</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {users, addUser, updateUser, deleteUser} from "@/api/user";
export default {
data() {
return {
dialogTitle: '',
searchParam: '',
records: [],
dialogVisible: false,
form: {},
formLabelWidth: '80px',
totalItems: 0,
currentPage: 1,
pageSize: 10,
};
},
mounted() {
this.currentPage = 1
this.loadData()
},
methods: {
fetchData() {
this.loadData()
},
//加载数据
loadData() {
users(this.searchParam, this.currentPage, this.pageSize).then(res => {
this.records = res.data.data.records
this.totalItems = res.data.data.total
})
},
handleAdd() {
this.dialogTitle = '新增用户'
this.dialogVisible = true;
this.form = {};
},
handleEdit(record) {
this.dialogTitle = '编辑用户'
this.dialogVisible = true;
this.form = {...record};
},
handleDelete(record) {
deleteUser(record.id).then(res => {
this.$message(res); // 使用封装的 $message 函数
})
},
handleSave() {
if (this.form.id) {
updateUser(this.form.id, this.form).then(res => {
// console.log(res.data.message)
this.$message(res); // 使用封装的 $message 函数
})
} else {
addUser(this.form).then(res => {
console.log(res.data.message)
this.$message(res); // 使用封装的 $message 函数
})
}
this.dialogVisible = false;
this.loadData()
},
handleCurrentChange(page) {
this.currentPage = page;
this.loadData();
},
handleSizeChange(size) {
this.pageSize = size;
this.loadData();
},
}
}
</script>
<style scoped>
.tours-container {
padding: 20px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
}
.dialog-footer {
text-align: right;
}
.header-title {
font-size: 18px;
font-weight: bold;
}
.header-controls {
display: flex;
align-items: center;
}
.search-input {
width: 300px;
margin-right: 10px; /* Adjust spacing between input and buttons */
}
</style>
然后修改Users.vue界面
3 完成 Profile.vue界面
<template>
<div class="profile-settings">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>个人设置</span>
</div>
<el-form :model="form" label-width="100px" :rules="rules" ref="profileForm">
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" disabled></el-input>
</el-form-item>
<el-form-item label="真实姓名" prop="realname">
<el-input v-model="form.realname" ></el-input>
</el-form-item>
<!-- 年龄输入框 -->
<el-form-item label="年龄" >
<el-input-number
v-model="form.age"
:min="0"
:max="120"
size="small"
></el-input-number>
</el-form-item>
<!-- 职业选择框 -->
<el-form-item label="职业" >
<el-select v-model="form.job" placeholder="请选择职业">
<el-option label="学生" value="学生"></el-option>
<el-option label="公务员" value="公务员"></el-option>
<el-option label="律师" value="律师"></el-option>
<el-option label="IT工程师" value="IT工程师"></el-option>
<el-option label="外卖员" value="外卖员"></el-option>
<el-option label="文员" value="文员"></el-option>
</el-select>
</el-form-item>
<el-form-item label="签名" prop="intro">
<el-input type="textarea" v-model="form.intro"></el-input>
</el-form-item>
<el-form-item label="地址" prop="addr">
<el-input v-model="form.addr"></el-input>
</el-form-item>
<el-form-item label="手机" prop="phone">
<el-input v-model="form.phone"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">保存</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
import {get_one, updateUser} from "@/api/user";
export default {
data() {
return {
form: {},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
age: [
{ required: true, message: '请输入年龄', trigger: 'blur' }
],
profession: [
{ required: true, message: '请输入职业', trigger: 'blur' }
],
phone: [
{ required: true, message: '请输入手机号码', trigger: 'blur' },
{ pattern: /^1[3456789]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }
],
email: [
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
{ type: 'email', message: '邮箱格式不正确', trigger: 'blur' }
]
}
};
},
mounted() {
const user = JSON.parse(localStorage.getItem('user'));
get_one(user.id).then(res=>{
// console.log(res.data)
this.form = res.data.data
})
},
methods: {
onSubmit() {
this.$refs.profileForm.validate((valid) => {
if (valid) {
updateUser(this.form.id, this.form).then(res => {
this.$message(res)
})
} else {
return false;
}
});
},
}
};
</script>
<style scoped>
.profile-settings {
padding: 20px;
}
</style>
4 项目小结
通过十二期的文章我们终于完成了旅游景点数据分析系统,下面来验收下成果,下面是课程涉及知识点的思维导图:
以下是我们完成的系统界面效果:
4.1 登录界面 & 注册界面
4.2 数据大屏界面 & 布局
4.3 景点管理界面