vue3+flask+sqlite前后端项目实战

基础环境安装

pycharm

下载地址:

https://www.jetbrains.com/zh-cn/pycharm/download/?section=windows

vscode

下载地址

https://code.visualstudio.com/docs/?dv=win64user

python

下载地址

https://www.python.org/downloads/windows/

Node.js(含npm)

下载地址

https://nodejs.org (推荐LTS版本)

后端项目

项目结构

复制代码
api/
├── models/
│   ├── __init__.py
│   └── user_model.py
├── dao/
│   ├── __init__.py
│   └── user_dao.py
├── instance/
│   └── app.db
├── routes/
│   ├── __init__.py
│   └── user_route.py
├── utils/
│   ├── __init__.py
│   └── sqlite3_util.py
├── config.py
├── run.py
└── requirements.txt

requirements.txt

blinker1.8.2
click8.1.8

colorama0.4.6
Flask3.0.0

Flask-Cors5.0.0
Flask-SQLAlchemy3.1.1

greenlet3.1.1
importlib_metadata8.5.0

itsdangerous2.2.0
Jinja23.1.6

MarkupSafe2.1.5
SQLAlchemy2.0.40

typing_extensions4.13.2
Werkzeug3.0.1

zipp==3.20.2
①生成 requirements.txt

pip freeze > requirements.txt

②基于 requirements.txt 安装

pip install -r requirements.txt

daos/user_dao.py

python 复制代码
import sqlite3
from werkzeug.security import generate_password_hash

class UserDAO:
    @staticmethod
    def get_connection():
        return sqlite3.connect('instance/app.db')

    @staticmethod
    def get_all_users():
        conn = UserDAO.get_connection()
        cursor = conn.cursor()
        cursor.execute("SELECT id, username FROM users")
        users = cursor.fetchall()
        conn.close()
        return [{'id': row[0], 'username': row[1]} for row in users]

    @staticmethod
    def create_user(username, password):
        conn = UserDAO.get_connection()
        cursor = conn.cursor()

        cursor.execute("SELECT id FROM users WHERE username = ?", (username,))
        if cursor.fetchone():
            conn.close()
            return None

        password_hash = generate_password_hash(password)
        cursor.execute("INSERT INTO users (username, password_hash) VALUES (?, ?)", (username, password_hash))
        conn.commit()
        conn.close()

        return {'username': username}

    @staticmethod
    def delete_user(user_id):
        conn = UserDAO.get_connection()
        cursor = conn.cursor()
        cursor.execute("DELETE FROM users WHERE id = ?", (user_id,))
        conn.commit()
        success = cursor.rowcount > 0
        conn.close()
        return success

models/user_model.py

python 复制代码
import sqlite3

def init_db():
    conn = sqlite3.connect('instance/app.db')
    cursor = conn.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            username TEXT UNIQUE NOT NULL,
            password_hash TEXT NOT NULL
        )
    ''')
    conn.commit()
    conn.close()

routes/user_route.py

python 复制代码
from flask import Blueprint, request, jsonify
from api.daos.user_dao import UserDAO

bp = Blueprint('user', __name__)

@bp.route('/users', methods=['GET'])
def get_users():
    users = UserDAO.get_all_users()
    return jsonify(users)

@bp.route('/register', methods=['POST'])
def register():
    data = request.json
    username = data.get('username')
    password = data.get('password')
    user = UserDAO.create_user(username, password)
    if user:
        return jsonify(user), 201
    else:
        return jsonify({'error': 'User already exists'}), 409

@bp.route('/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
    success = UserDAO.delete_user(user_id)
    if success:
        return jsonify({'message': 'User deleted successfully'}), 200
    else:
        return jsonify({'error': 'User not found'}), 404

utils/sqlite3_util.py

python 复制代码
import sqlite3

def init_db():
    conn = sqlite3.connect('instance/app.db')
    cursor = conn.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            username TEXT UNIQUE NOT NULL,
            password_hash TEXT NOT NULL
        )
    ''')
    conn.commit()
    conn.close()

run.py

python 复制代码
from flask import Flask
from flask_cors import CORS
from routes.user_route import bp as user_bp
from utils.sqlite3_util import init_db

app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*"}})

app.config.from_object('config.Config')



app.register_blueprint(user_bp, url_prefix='/api')

if __name__ == '__main__':
    init_db()
    app.run(debug=True, host='0.0.0.0')

启动

python app.py // 或者直接右键该文件运行

前端项目

新建vue项目

python 复制代码
npm create vue@latest
ui

cd ui
npm install

npm install vue-router@4 axios element-plus @element-plus/icons-vue

pycharm打开效果

修改 src/assets/main.css

css 复制代码
/* 导入基础样式文件 */
@import './base.css';

/*
 * 主应用容器样式
 * 作用:包裹整个Vue应用的根容器
 */
#app {
  margin: 0 auto;                   /* 水平居中 */
  padding: 2rem;                    /* 内边距(会被下方规则覆盖) */
  font-weight: normal;              /* 继承基础样式字体粗细 */
  display: block !important;        /* 强制覆盖可能的grid布局,使用块级布局 */
  width: 100%;                      /* 撑满可用宽度 */
  grid-template-columns: 1fr 1fr;   /* 网格列定义(实际被block覆盖无效) */
  padding: 0 2rem;                  /* 重定义左右内边距(覆盖上方padding) */
}

/*
 * 链接及特殊文本样式
 * 作用:统一超链接和.green类元素的视觉效果
 */
a,
.green {
  text-decoration: none;            /* 去除下划线 */
  color: hsla(160, 100%, 37%, 1);   /* Vue品牌绿色 (HSL格式) */
  transition: 0.4s;                 /* 颜色过渡动画时长 */
  padding: 3px;                     /* 内边距增强可点击区域 */
}

/*
 * 悬停效果媒体查询
 * 作用:只在支持hover的设备上应用悬停效果
 */
@media (hover: hover) {
  a:hover {
    background-color: hsla(160, 100%, 37%, 0.2);  /* 半透明绿色背景 */
  }
}

修改 src/main.js

javascript 复制代码
/*
 * 样式资源导入
 * 作用:引入全局基础样式文件
 */
import './assets/main.css';

/*
 * Vue核心依赖导入
 * 作用:引入Vue框架核心功能
 */
import { createApp } from 'vue';      // 引入应用构造器

/*
 * 应用组件导入
 * 作用:引入根组件作为应用入口
 */
import App from './App.vue';          // 主应用组件

/*
 * 路由配置导入
 * 作用:引入路由管理系统
 */
import router from './router';        // 路由实例

/*
 * UI组件库导入
 * 作用:引入Element Plus及其样式
 */
import ElementPlus from 'element-plus';         // UI库核心
import 'element-plus/dist/index.css';           // UI库样式

/*
 * 应用初始化
 * 作用:创建并配置Vue应用实例
 */
const app = createApp(App);           // 创建应用实例

/*
 * 插件注册
 * 作用:集成路由功能
 */
app.use(router);                      // 安装路由插件

/*
 * UI库注册
 * 作用:集成Element Plus组件库
 */
app.use(ElementPlus);                 // 安装UI组件库

/*
 * 应用挂载
 * 作用:将应用渲染到DOM
 */
app.mount('#app');                    // 挂载到DOM节点

修改 src/App.vue

清空默认内容,改为空白模板

javascript 复制代码
<template>
  <!--
   * 路由视图容器
   * 作用:渲染当前路由匹配的组件
   * 技术:Vue Router 核心组件
   -->
  <router-view />
</template>

<script>
export default {
  /*
   * 组件标识
   * 作用:用于开发者工具调试和递归组件引用
   * 命名规范:通常使用帕斯卡命名法(PascalCase)
   */
  name: 'App'
};
</script>

<style>
/*
 * 全局基础样式
 * 作用:影响整个应用的默认样式重置
 */

/*
 * 主体样式重置
 * 作用:清除浏览器默认样式,设置基准字体
 */
body {
  margin: 0;                  /* 清除默认外边距 */
  font-family: Arial,         /* 首选字体 */
    sans-serif;               /* 通用字体族后备 */
}
</style>

src/views/Dashboard.vue

javascript 复制代码
<template>
  <div class="dashboard-container">
    <!-- 顶部导航菜单 -->
    <el-menu
      mode="horizontal"
      :default-active="activeMenu"
      :collapse="isCollapsed"
      class="el-menu-horizontal-demo"
      @select="handleMenuSelect"
      background-color="#ffffff"
      text-color="#606266"
      active-text-color="#409EFF"
    >
      <!-- 1. 三维数据平台 -->
      <el-sub-menu index="1">
        <template #title>
          <i class="el-icon-location"></i>
          <span v-show="!isCollapsed">三维数据平台</span>
        </template>
        <el-menu-item index="1-1" @click="goToModelRelease">
          <i class="el-icon-monitor"></i>
          <span>模型发布对比</span>
        </el-menu-item>
        <el-menu-item index="1-2" @click="goToParseInfo">
          <i class="el-icon-connection"></i>
          <span>获取解析异常信息</span>
        </el-menu-item>
      </el-sub-menu>

      <!-- 2. 测试管理 -->
      <el-sub-menu index="2">
        <template #title>
          <i class="el-icon-cpu"></i>
          <span v-show="!isCollapsed">测试管理</span>
        </template>
        <el-menu-item index="2-1" @click="goToTestFlow">
          <i class="el-icon-guide"></i>
          <span>测试流程</span>
        </el-menu-item>
        <el-menu-item index="2-2" @click="goToTaskReminder">
          <i class="el-icon-guide"></i>
          <span>任务提醒</span>
        </el-menu-item>
      </el-sub-menu>


      <!-- 3. 工具集 -->
      <el-sub-menu index="3">
        <template #title>
          <i class="el-icon-s-tools"></i>
          <span v-show="!isCollapsed">实用工具</span>
        </template>
        <el-menu-item index="3-1" @click="goToJsonFormat">
          <i class="el-icon-document"></i>
          <span>JSON格式化</span>
        </el-menu-item>
        <el-menu-item index="3-2" @click="goToKafkaManager">
          <i class="el-icon-document"></i>
          <span>Kafka连接测试</span>
        </el-menu-item>
      </el-sub-menu>


      <!-- 4. 学生管理 -->
      <el-sub-menu index="4">
        <template #title>
          <i class="el-icon-user"></i>
          <span v-show="!isCollapsed">学生管理</span>
        </template>
        <el-menu-item index="4-1" @click="goToStudentList">
          <i class="el-icon-user-solid"></i>
          <span>学生列表</span>
        </el-menu-item>
        <el-menu-item index="4-2" @click="goToStudentStats">
          <i class="el-icon-data-analysis"></i>
          <span>学生统计</span>
        </el-menu-item>
      </el-sub-menu>



      <!-- 5. 新增的系统管理菜单 -->
      <el-sub-menu index="5">
        <template #title>
          <i class="el-icon-setting"></i>
          <span v-show="!isCollapsed">系统管理</span>
        </template>
        <el-menu-item index="5-1" @click="goToUserManage">
          <i class="el-icon-user"></i>
          <span>用户管理</span>
        </el-menu-item>
        <el-menu-item index="5-2" @click="goToRoleManage">
          <i class="el-icon-s-custom"></i>
          <span>角色管理</span>
        </el-menu-item>
        <el-menu-item index="5-3" @click="goToSystemLog">
          <i class="el-icon-document"></i>
          <span>系统日志</span>
        </el-menu-item>
        <el-menu-item index="5-4" @click="goToSystemConfig">
          <i class="el-icon-operation"></i>
          <span>系统配置</span>
        </el-menu-item>
      </el-sub-menu>
    </el-menu>

    <!-- 主内容区域 -->
    <div class="content">
      <el-card class="welcome-card">
        <h1>{{ welcomeTitle }}</h1>
        <p>{{ welcomeMessage }}</p>

        <!-- 快捷访问区域 -->
        <div v-if="showQuickAccess" class="quick-access">
          <h3>常用功能</h3>
          <el-space wrap>
            <el-tag
              v-for="(action, index) in quickActions"
              :key="index"
              type="info"
              effect="plain"
              class="quick-tag"
              @click="action.handler"
            >
              <i :class="action.icon"></i>
              {{ action.label }}
            </el-tag>
          </el-space>
        </div>
      </el-card>
    </div>
  </div>
</template>

<script>
import { ref, onMounted, onUnmounted, computed } from 'vue'
import { useRouter } from 'vue-router'

export default {
  setup() {
    const router = useRouter()

    // 响应式状态
    const activeMenu = ref('1-1')
    const isCollapsed = ref(false)
    const showQuickAccess = ref(true)
    const welcomeTitle = ref('欢迎使用管理系统')
    const welcomeMessage = ref('请从上方菜单选择您需要的功能')

    // 路由跳转方法
    const goToModelRelease = () => router.push('/model_release')
    const goToParseInfo = () => router.push('/parse_info')
    const goToTestFlow = () => router.push('/TestFlow')
    const goToTaskReminder = () => router.push('/task_reminder')
    const goToStudentList = () => router.push('/StudentList')
    const goToStudentStats = () => router.push('/StudentStats')
    const goToDbCompare = () => router.push('/dbcompare')
    const goToJsonFormat = () => router.push('/json_format')
    const goToKafkaManager = () => router.push('/kafka_manager')


    // 新增的系统管理路由方法
    const goToUserManage = () => router.push('/user-manage')
    const goToRoleManage = () => router.push('/role-manage')
    const goToSystemLog = () => router.push('/system-log')
    const goToSystemConfig = () => router.push('/system-config')

    // 快捷操作列表(包含新增的系统管理快捷方式)
    const quickActions = computed(() => [
      { icon: 'el-icon-monitor', label: '模型发布数据对比', handler: goToModelRelease },
      { icon: 'el-icon-user-solid', label: '测试流程', handler: goToTestFlow },
      { icon: 'el-icon-document', label: '待办任务', handler: goToTaskReminder },
      { icon: 'el-icon-setting', label: 'Json格式化', handler: goToJsonFormat }
    ])

    // 响应式处理屏幕尺寸变化
    const checkScreen = () => {
      isCollapsed.value = window.innerWidth < 768
      showQuickAccess.value = window.innerWidth > 576
    }

    // 菜单选择处理
    const handleMenuSelect = (index) => {
      activeMenu.value = index
    }

    // 生命周期钩子
    onMounted(() => {
      window.addEventListener('resize', checkScreen)
      checkScreen() // 初始化检查
    })

    onUnmounted(() => {
      window.removeEventListener('resize', checkScreen)
    })

    return {
      activeMenu,
      isCollapsed,
      showQuickAccess,
      welcomeTitle,
      welcomeMessage,
      quickActions,
      handleMenuSelect,
      // 三维数据平台
      goToModelRelease,
      goToParseInfo,
      // 测试管理
      goToTestFlow,
      goToTaskReminder,
      // 实用工具
      goToJsonFormat,
      goToKafkaManager,
      // 学生管理
      goToStudentList,
      goToStudentStats,
      goToDbCompare,
      // 系统管理
      goToUserManage,
      goToRoleManage,
      goToSystemLog,
      goToSystemConfig
    }
  }
}
</script>

<style scoped>
/* 主容器样式 */
.dashboard-container {
  padding: 10px;
}

/* 导航菜单样式 */
.el-menu-horizontal-demo {
  height: 36px;
  line-height: 36px;
  border-bottom: 1px solid #e6e6e6;
  margin-bottom: 20px;
}

.el-menu-item.is-active {
  background-color: var(--el-color-primary-light-9) !important;
  border-bottom: 2px solid var(--el-color-primary) !important;
}

/* 内容区域布局 */
.content {
  padding: 20px;
  display: flex;
  justify-content: center;
  min-height: calc(100vh - 160px);
}

/* 欢迎卡片样式 */
.welcome-card {
  width: 100%;
  max-width: 800px;
  text-align: center;
  padding: 40px;
  border-radius: 8px;
}

/* 快捷访问区域 */
.quick-access {
  margin-top: 30px;
  padding-top: 20px;
  border-top: 1px dashed #eee;
}

.quick-tag {
  cursor: pointer;
  padding: 0 15px;
  height: 32px;
  line-height: 32px;
  transition: all 0.3s;
}

.quick-tag:hover {
  color: #409EFF;
  border-color: #409EFF;
  transform: translateY(-2px);
}

/* 响应式设计 */
@media (max-width: 768px) {
  .el-menu-item,
  .el-submenu__title {
    padding: 0 12px !important;
  }

  .welcome-card {
    padding: 20px;
  }
}

@media (max-width: 576px) {
  .dashboard-container {
    padding: 10px;
  }

  .content {
    min-height: calc(100vh - 120px);
  }
}
</style>

src/views/User.vue

javascript 复制代码
<template>
  <div>
    <h1>用户注册</h1>
    <el-form @submit.prevent="handleSubmit" inline>
      <el-form-item>
        <el-input v-model="username" placeholder="用户名"></el-input>
      </el-form-item>
      <el-form-item>
        <el-input v-model="password" type="password" placeholder="密码"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="handleSubmit">注册</el-button>
      </el-form-item>
    </el-form>

    <h2>已注册用户</h2>
    <el-table :data="users" style="width: 100%">
      <el-table-column prop="id" label="ID" width="50"></el-table-column>
      <el-table-column prop="username" label="用户名" width="180"></el-table-column>
      <el-table-column label="操作" width="120">
        <template #default="scope">
          <el-button type="text" @click="handleDelete(scope.row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
import 'element-plus/dist/index.css'

const username = ref('')
const password = ref('')
const users = ref([])

const fetchUsers = async () => {
  const response = await axios.get('http://192.168.1.138:5000/api/users')
  users.value = response.data
}

const handleSubmit = async () => {
  await axios.post('http://192.168.1.138:5000/api/register', {
    username: username.value,
    password: password.value
  })
  username.value = ''
  password.value = ''
  await fetchUsers()
}

const handleDelete = async (id) => {
  await axios.delete(`http://192.168.1.138:5000/api/users/${id}`)
  await fetchUsers()
}

onMounted(() => {
  fetchUsers()
})
</script>

src/router/index.js

javascript 复制代码
import { createRouter, createWebHistory } from 'vue-router'
import Dashboard from '../views/Dashboard.vue'
import User from '../views/User.vue'

const routes = [
  { path: '/', redirect: '/Dashboard' }, // 默认重定向
  { path: '/dashboard', component: Dashboard },
  { path: '/user', component: User },

  // 其他路由...
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

前端运行测试

npm run dev

前端启动后默认跳转的效果

用户注册


使用navicat连接查看数据库

相关推荐
凛铄linshuo21 分钟前
爬虫简单实操2——以贴吧为例爬取“某吧”前10页的网页代码
爬虫·python·学习
牛客企业服务24 分钟前
2025年AI面试推荐榜单,数字化招聘转型优选
人工智能·python·算法·面试·职场和发展·金融·求职招聘
胡斌附体36 分钟前
linux测试端口是否可被外部访问
linux·运维·服务器·python·测试·端口测试·临时服务器
likeGhee1 小时前
python缓存装饰器实现方案
开发语言·python·缓存
项目題供诗1 小时前
黑马python(二十五)
开发语言·python
读书点滴2 小时前
笨方法学python -练习14
java·前端·python
笑衬人心。2 小时前
Ubuntu 22.04 修改默认 Python 版本为 Python3 笔记
笔记·python·ubuntu
蛋仔聊测试2 小时前
Playwright 中 Page 对象的常用方法详解
python
前端付豪2 小时前
17、自动化才是正义:用 Python 接管你的日常琐事
后端·python
jioulongzi2 小时前
记录一次莫名奇妙的跨域502(badgateway)错误
开发语言·python