Vue3+ElementPlus+Bootstrap实现表格页面展示

883 阅读2分钟

Flask+Vue3 Full Stack

封面

0. 环境

  1. OS: Ubuntu20.04
  2. pipenv: 2023.7.4
  3. Flask: 2.0.1
  4. nodejs: v18.16.1基于 Chrome V8 引擎的JS运行环境
  5. npm: 6.14.4node.js 的包管理工具
  6. Vue:@vue/cli 5.0.8
  7. element-plus: 2.3.7
  8. bootstrap: 4.6.1
  9. pinia: 2.1.3
  1. npm国内换源: sudo npm config set registry https://registry.npm.taobao.org
  2. 为了升级node全局安装n版本管理工具n:sudo npm install n -g (1)su root (2)export N_NODE_MIRROR=https://npm.taobao.org/mirrors/node (3) n stable
  3. 安装Vue:sudo npm install -g vue 安装Vue脚手架sudo npm install -g @vue/cli

1. 项目结构初始化

🐵backend

后端工作:python环境,使用虚拟环境

~/Desktop/flask-vue-tutorial/backend
$ pipenv shell
$ nano requirements.txt
$ pipenv install -r requirements.txt
$ nano main.py
$ python main.py

依赖 hello 按照惯例,Hello一下🖐️ hello

💰frontend

~/Desktop/flask-vue-tutorial/frontend
$ npm init vue@latest
$ cd gameList/
$ npm install
$ npm run format
$ npm run dev
  • 修改vite.config.js 配置 还可设置port:8086,设置open: true让服务启动时自动打开浏览器
  • Hello一下🖐️ Hello

2. axios配置

npm i axios

配置路由

路由

在前端通过axios请求后台服务

shark

后台响应

shark

前台回显结果,合作愉快🤝

ping

3. 🍃页面设计

引入Element Plus

  • 安装: npm install element-plus --save
  • 配置按需导入: npm install -D unplugin-vue-components unplugin-auto-import
  • vite.config.js中配置: plugin
  • 注释原来的样式文件main.css 注释

✌单页面设计

frontend/gameList/src/components/Games.vue

<script setup>
import {ref} from 'vue';
import axios from 'axios';
const tableData = ref()
const path = 'http://192.168.133.130:5000/games'
axios.get(path).then((res)=>{
    tableData.value = res.data.games;
})
</script>
<template>
    <div>
        <el-container>
            <el-header>
                <el-row justify="center">
                    <el-text tag="b" type="primary" size="large">Games Library🕹️</el-text>
                </el-row>
                <el-divider/>
            </el-header>
            <el-main>
                <!-- Alert Message -->
                <el-row justify="center">
                    <el-button plain type="success" @click="addGame">Add Game</el-button>
                </el-row>
                <el-table :data="tableData" stripe>
                    <el-table-column prop="title" label="Title" min-width="80" align="center"/>
                    <el-table-column prop="genre" label="Genre" min-width="80" align="center"/>
                    <el-table-column prop="played" label="Played?" min-width="80" align="center"/>
                    <el-table-column fixed="right" label="Actions" min-width="150" align="center">
                        <template #default>
                            <el-button round type="primary" size="small" @click="updateGame"
                            >update</el-button
                            >
                            <el-button round type="danger" size="small" @click="deleteGame">delete</el-button>
                        </template>
                    </el-table-column>
                </el-table>
            </el-main>
            <el-footer>
                <el-row justify="center">
                    <el-text type="info">Over</el-text>
                </el-row>
            </el-footer>
        </el-container>
    </div>
</template>

后台响应💡

backend/main.py hbtl

UI效果:

UI效果

尝试用用bootstrap

npm install bootstrap@4.6.1 --save ![zgjw](img-blog.csdnimg.cn/48dfe75b697… =400x40)

引入bootswatch CDN

bootswatch CDN 选择Sketchy sketchy

思考🤔:

  1. bootstrap和elementplus搭配是否可行?
  2. element-plus的表格如何自定义样式?
  3. 怎么加背景图,怎么让表格透明显示?

背景图:

背景

对样式进行魔改⌨️

<script setup>
import {ref} from 'vue';
import axios from 'axios';
const tableData = ref()
const path = 'http://192.168.133.130:5000/games'
axios.get(path).then((res)=>{
    tableData.value = res.data.games;
})
</script>
<template>
    <div id="building">
    <br/>
    <div class="jumbotron vertical-center" id="content">
        <el-container>
            <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@4.5.2/dist/sketchy/bootstrap.min.css" integrity="sha384-RxqHG2ilm4r6aFRpGmBbGTjsqwfqHOKy1ArsMhHusnRO47jcGqpIQqlQK/kmGy9R" crossorigin="anonymous">
            <el-header>
                <!-- bootswatch CDN -->
                <el-row class="nav">
                    <h2 class="logo">To Play🕹️</h2>
                    <button class="btnAdd-popup" @click="addGame">Add</button>
                    <el-divider/>
                </el-row>
            </el-header>
            <el-main>
                <!-- Alert Message -->
                <hr><br>
                <el-table 
                    :data="tableData"  
                    style="max-width:800px;margin:0 auto"
                    >
                    <el-table-column prop="title" label="Title" min-width="80" align="center"/>
                    <el-table-column prop="genre" label="Genre" min-width="80" align="center"/>
                    <el-table-column prop="played" label="Played?" min-width="80" align="center"/>
                    <el-table-column label="Actions" min-width="150" align="center">
                        <template #default>
                            <el-button round type="primary" size="small" @click="updateGame"
                            >update</el-button
                            >
                            <el-button round type="danger" size="small" @click="deleteGame">delete</el-button>
                        </template>
                    </el-table-column>
                </el-table>
            </el-main>
            <el-footer>
                <hr><br>
                <el-row justify="center">
                    <el-text tag="ins" class="footer">Copyright &copy; . All Rights Reserved 2023.</el-text>
                </el-row>
            </el-footer>
        </el-container>
    </div>
    </div>
</template>
<style scoped>
#building{
    background:linear-gradient(rgba(233, 226, 226, 0.251), rgba(228, 223, 223, 0.551)), url('../assets/wallhaven1.jpg') no-repeat ;
    width:100%;		
    height:100%;		
    position:fixed;
    background-size:100% 100%;
}
#content{
    margin-left:20px;
    margin-right:20px;
}
.nav{
    justify-content: space-between;
    align-items: center;
    z-index: 99;
}
.logo{
    font-size: 2em;
    font-weight: bolder;
    color : #fff;
    user-select: none;
    margin-left:45%;
}
.nav .btnAdd-popup{
    width: 130px;
    height: 50px;
    background: transparent;
    border: 2px solid #fff;
    outline: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 1.1em;
    color: #fff;
    font-weight: 500;
    margin-right:40px;
    transition: .5s;
}
.nav .btnAdd-popup:hover{
    background: #fff;
    color: #162938;
}
.footer{
    font-size:1.1em;
    font-weight: bolder;
    color:antiquewhite;
}
:deep(.el-table){
    border: 1px solid black;
    background-color: transparent;
}
:deep(.el-table th){
    background-color: rgba(223, 168, 168, 0.5);
}
:deep(.el-table tr){
    background-color: transparent;
}
:deep(.el-table td){
    background-color: rgba(255,255,255,0.5);
}
:deep(.el-table th .cell){
    color:#fff;
    font-weight: bold;
    font-size:large;
}
:deep(.el-table td .cell){
    color:green;
    font-weight: bold;
    font-size:large;
}
</style>

🤟最终UI效果:

toplay

4. 🕹️按钮点击事件

后台API

@app.route('/games', methods=['GET', 'POST'])
def allGames():
	response_object = {'status': 'success'}
	if request.method == 'POST':
		post_data = request.get_json()
		GAMES.append({
      			'id': uuid.uuid4().hex,
			'title': post_data.get('title'),
			'genre': post_data.get('genre'),
			'played': post_data.get('played')
		})
		response_object['message'] = 'Game Added!'
	else:
		response_object['games'] = GAMES
	return jsonify(response_object)

@app.route('/games/<game_id>', methods=['PUT','DELETE'])
def single_game(game_id):
  response_object = {'status': 'success'}
  if request.method == 'PUT':
    post_data = request.get_json()
    remove_game(game_id)
    GAMES.append({
      'id': uuid.uuid4().hex,
      'title': post_data.get('title'),
      'genre': post_data.get('genre'),
      'played': post_data.get('played')
	})
    response_object['message'] = 'Game updated!'
  if request.method == "DELETE":
    remove_game(game_id)
    response_object['message'] = 'Game removed!'
  return jsonify(response_object)

def remove_game(game_id):
  for game in GAMES:
    if game['id'] == game_id:
      GAMES.remove(game)
      return True
  return False

增➕

  1. dialog变量控制弹窗显示, form变量绑定表单数据 d;yi

  2. click触发弹窗 click

  3. dialog的具体样式 dvhw 样式

  4. 点击submit后,打包数据,传给API submit

  5. 前台API的实现, 发送请求,刷新数据,消息提示 在这里插入图片描述 效果

删❌

  1. 思考:点击删除按钮,需要传递当前行的id,怎么传递? 答: 通过作用域插槽 作用域插槽
  2. 与后台通信,然后更新列表,回显消息 处理

改✂️

  1. dialog变量控制弹窗显示, form变量绑定表单数据 bmld
  2. click触发弹窗, 使用作用域插槽传递当前行信息 插槽 传参
  3. dialog的具体样式 对话
  4. 点击submit后,打包数据,传给API 打包
  5. API的实现, 发送请求,刷新数据,消息提示 uixm

5. 预览

预览网址📖 部署在Netlify上 为了简单,去掉了flask后端,使用pinia来进行状态管理

什么是Netlify? 一个现代网站自动化系统,只要在本机Git中编写前端代码,然后推送它,网站就能完美地对外呈现,类似网站托管工具

部署

6. 源码

源码🐴在github可找到