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

发布于:2025-05-13 ⋅ 阅读:(9) ⋅ 点赞:(0)

基础环境安装

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
click
8.1.8
colorama0.4.6
Flask
3.0.0
Flask-Cors5.0.0
Flask-SQLAlchemy
3.1.1
greenlet3.1.1
importlib_metadata
8.5.0
itsdangerous2.2.0
Jinja2
3.1.6
MarkupSafe2.1.5
SQLAlchemy
2.0.40
typing_extensions4.13.2
Werkzeug
3.0.1
zipp==3.20.2

①生成 requirements.txt
pip freeze > requirements.txt
②基于 requirements.txt 安装
pip install -r requirements.txt

daos/user_dao.py

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

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

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

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

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项目

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

/* 导入基础样式文件 */
@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

/*
 * 样式资源导入
 * 作用:引入全局基础样式文件
 */
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

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

<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

<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

<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

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连接查看数据库

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到