从0开始搭建vue + flask 旅游景点数据分析系统(十二)【完结篇】:用户管理之增删改查、用户信息更新

这一期继续完成用户管理的增删改查和登录用户修改自己信息的功能,首先完成后端接口的功能。

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 景点管理界面

在这里插入图片描述
在这里插入图片描述

4.4 用户管理界面

在这里插入图片描述

4.5 个人信息修改界面

在这里插入图片描述

  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麦麦大数据

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值