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

news2024/11/13 11:41:27

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

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 个人信息修改界面

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2042687.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

离线安装prometheus与Grafana实现可视化监控

简介 prometheus 是一个专为云环境设计的开源系统监控和警报工具&#xff0c;它收集并存储多维度的时间序列数据&#xff0c;通过PromQL查询语言提供强大的数据检索能力&#xff0c;并支持可视化及警报功能。而 Grafana 则是一个开源的数据可视化平台&#xff0c;能够与包括Pr…

2024软件测试面试官在面试的时候会做些什么?

虽然没有了金九银十&#xff0c;但是公司的测试HC还是有完全锁死&#xff0c;断断续续的也在帮着面试一些人。同时星球上也有很多人在关注面试的问题&#xff0c;本文就以自己的经验&#xff0c;从面试官的角度&#xff0c;聊聊面试过程中的那些事。 一、笔试面试官 在面试开…

使用Flv.js无法播放视频

背景 由于项目需要&#xff0c;搭建了一套SRS直播服务&#xff0c;通过直播录制将视频报错并在本地播放。视频存储的格式为flv&#xff0c;所以使用flv.js插件来播放。测试时发现录制的视频无法播放&#xff0c;经过排查找到原因。 报错信息 控制台并无明显报错&#xff0c;…

Keepalived高可用集群--几个实验带你认识集群的坚实后盾

一、KeepAlived的发展 Keepalived起初是为LVS设计的&#xff0c;专门用来监控集群系统中各个服务节点的状态&#xff0c;它根据TCP/IP参考模型的第三、第四层、第五层交换机制检测每个服务节点的状态&#xff0c;如果某个服务器节点出现异常&#xff0c;或者工作出现故障&#…

神经串联式语音转换:对基于串联的单次语音转换方法的再思考 论文笔记

NEURAL CONCATENATIVE SINGING VOICE CONVERSION: RETHINKING CONCATENATION-BASED APPROACH FOR ONE-SHOT SINGING VOICE CONVERSION 笔记 发现问题&#xff1a; 在any-to-any的转换中,由于内容和说话人音色的解耦不足,导致源说话人的音色部分仍保留在转换后的音频中&#x…

Dom4j详细介绍

Dom4j 1.1 解析概览 将数据存储为XML格式后&#xff0c;程序化地访问这些数据变得至关重要。虽然Java基础的IO操作能够实现这一目标&#xff0c;但这一过程往往既复杂又繁琐&#xff0c;尤其是在处理大型文件或需要频繁读写操作的场景下。为了解决这些问题&#xff0c;开发者…

多条折线图修改图例以及自定义tooltip

在图例后面添加所有数据之和修改之后 series 中的name之后导致tooltip也加上了重新自定义tooltip&#xff0c;去掉总量统计 核心代码 监听数据改变计算总量修改name字段自定义 tooltip // 计算每条线的总和 const sum1 this.VALUE1.reduce((acc, val) > acc val, 0); co…

【python】在Python中读取和加解密PDF文件的详细教程与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Python酷库之旅-第三方库Pandas(082)

目录 一、用法精讲 341、pandas.Series.str.startswith方法 341-1、语法 341-2、参数 341-3、功能 341-4、返回值 341-5、说明 341-6、用法 341-6-1、数据准备 341-6-2、代码示例 341-6-3、结果输出 342、pandas.Series.str.strip方法 342-1、语法 342-2、参数 …

go语言源码解读之数据结构堆

概述 堆(heap)&#xff0c;是一种计算中常用的数据结构。本文我们将探讨对的特性、实现细节以及实际应用场景。 基本概念 堆是一种特殊的完全二叉树。堆分为大顶堆与小顶堆。 大顶堆的特点是&#xff0c;父节点的值总是大于或等于其子节点的值。 小顶堆的特点是&#xff0c…

SocialFi新纪元:Match革新金融与社交的融合艺术

前言 Match在SocialFi领域的创新探索&#xff0c;或将金融与社交的融合艺术推向新的高度&#xff01; SocialFi自带"爆点”属性&#xff0c;Web 3.0巨型流量池 统计数据显示&#xff0c;2023年第三季度&#xff0c;全球约有54亿人活跃在互联网上&#xff0c;而社交媒体领…

江协科技STM32学习笔记(第13章 WDG看门狗)

第13章 WDG看门狗 13.1 WDG看门狗 13.1.1 WDG简介 看门狗就是程序运行的一个保障措施&#xff0c;我们得在程序中定期地喂狗&#xff0c;如果程序卡死了&#xff0c;没有在规定的时间里喂狗&#xff0c;那么看门狗硬件电路就会自动帮我们复位一下&#xff0c;防止程序长时间…

<keep-alive> 一分钟了解

<keep-alive> 一分钟了解 <keep-alive> 是 Vue.js 提供的一个抽象组件&#xff0c;它的主要用途是在页面或组件切换时保留其状态&#xff0c;避免重复执行昂贵的渲染操作&#xff0c;从而提升应用性能。 文章目录 <keep-alive> 一分钟了解 一、 <keep-ali…

Ubuntu如何实现每天定时关机

要在Ubuntu中实现每天定时关机&#xff0c;你可以使用cron来安排定时任务。以下是具体的步骤&#xff1a; 步骤 1: 创建脚本 打开终端。使用文本编辑器创建一个新的文件。例如&#xff1a; nano ~/shutdown_script.sh 步骤 2: 编写脚本 在编辑器中输入以下内容&#xff1a…

华府便利店信息管理系统

TOC springboot239华府便利店信息管理系统 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规…

花四小时,写了个在线实时绘制等值面图小软件,明晚上线,喜欢的小伙伴关注哦

科研党的福音&#xff0c;绘图再也不需要安装一堆软件了&#xff0c;可以在线绘图了&#xff1b; 只需要传入绘制的区间、色值、以及所需要绘制的数据就可以直接出图了&#xff0c;可绘制各种等值面图&#xff0c;比如降水分布&#xff0c;高温分布&#xff0c;人口分布&#x…

文心快码真的很好用!!!

最近被身边的好友安利到了一个百度的新产品文心快码&#xff08;comate&#xff09;&#xff0c;没想到体验下来真的很好用&#xff0c;非常容易上手&#xff0c;解放了我的双手&#xff0c;提高了代码生产力&#xff01;可能有很多小伙伴不知道怎么使用comate,而我作为这类工具…

C语言-将n个数输入时顺序的逆序排列,用指针函数实现

一、题目要求&#xff1a; 将n个数输入时顺序的逆序排列&#xff0c;用指针函数实现 二、程序: #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() {int n;printf("请输入一共有多少数:\n");scanf("%d", &n);int arr[100], i;…

大模型之二十五-人工智能新纪元

人类社会正式从信息科技时代步入了人工智能时代&#xff0c;相比信息科技革命&#xff0c;人工智能科技革命的影响要深远的多&#xff0c;在这新旧交替剧烈变革期&#xff0c;绝大多数人都有机会。 为了更好的理解人工智能科技革命&#xff0c;首先我们首先梳理一下技术的发展…

使用VNC-viewer对树莓派5 远程连接桌面—详细记录笔记版

树莓派5 的远程桌面连接&#x1f680; 在完成了对树莓派镜像的安装&#xff0c;以及点亮了屏幕之后&#xff0c;接下来就是为开发做一些准备&#xff0c;就是配置树莓派5的远程的桌面的操作&#xff0c;因为如果不这样的话&#xff0c;我在PC上和树莓派系统上分别进行作业的时候…