RuoYi-Vue3-FastAPI框架的功能实现(下)
这段正好使用RuoYi-Vue3-FastAPI框架做了一个小系统平台,根据实际项目给出最后的总结篇。
补充部分功能(上传功能、如何补充展现字段)
上传功能
配置后端
首先创建表单,根据你的上传需要创建数据库表,然后利用代码生成功能生成一个表单界面(后端还是可以利用这里的自动生成方便我们改代码)。后端主要修改两个文件:(_前面是代码生成的)
修改update_service.py
同样单独建立上传模块即可
from sqlalchemy.ext.asyncio import AsyncSession
from typing import List
from exceptions.exception import ServiceExceptionfrom utils.common_util import CamelCaseUtil
from utils.excel_util import ExcelUtil
from sqlalchemy.orm import Session
from module_pon.update.entity.do.update_do import OltZhuzhang
# from models.excel import ExcelData
import pandas as pd# 📁 module_pon/update/service/update_service.py
class ExcelDataService:"""Excel数据操作服务类整合原有独立函数,保持代码风格统一"""@classmethodasync def create_excel_data(cls, db: AsyncSession, data: dict):"""创建单条Excel数据记录:param db: 异步数据库会话:param data: 数据字典:return: 创建的记录对象"""try:db_data = OltZhuzhang(**data)db.add(db_data)await db.commit()await db.refresh(db_data)return db_dataexcept Exception as e:await db.rollback()raise ServiceException(message=f"创建Excel数据失败: {str(e)}")@classmethodasync def create_excel_data_bulk(cls, db: AsyncSession, data_list: List[dict]):"""批量创建Excel数据记录:param db: 异步数据库会话:param data_list: 数据字典列表:return: 成功插入的记录数"""try:await db.run_sync(lambda session: session.bulk_insert_mappings(OltZhuzhang, data_list))await db.commit()return len(data_list)except Exception as e:await db.rollback()raise ServiceException(message=f"批量创建Excel数据失败: {str(e)}")@classmethodasync def read_excel_data(cls, db: AsyncSession, skip: int = 0, limit: int = 100,**filters):"""查询Excel数据记录:param db: 异步数据库会话:param skip: 跳过数量:param limit: 限制数量:param filters: 过滤条件:return: 查询结果列表"""try:query = await db.run_sync(lambda session: session.query(OltZhuzhang))if filters:query = await db.run_sync(lambda session: query.filter_by(**filters))result = await db.run_sync(lambda session: query.offset(skip).limit(limit).all())return [CamelCaseUtil.transform_result(item) for item in result]except Exception as e:raise ServiceException(message=f"查询Excel数据失败: {str(e)}")
修改update_controller.py
这里主要注意修改的地方就是,动态模板这里,因为上传是有模板的,我们要按照导入的数据制定模板,不然数据可能会存在导入不全或者报错。
df = pd.DataFrame(columns=["序号", "pon", "ip", "账期", "备注"])
如果界面只展示导入界面,那么就可以把其他的删除掉,仅仅保留上传的代码。
from fastapi import APIRouter, Depends, Form, Request
from module_admin.service.login_service import LoginService
from utils.log_util import logger
from utils.response_util import ResponseUtilfrom fastapi import APIRouter, UploadFile, File, HTTPException
from fastapi.responses import FileResponse
from typing import List
import pandas as pd
import io
from datetime import datetime
import os
from config.get_db import get_db
# 这里记得修改一下引入的方法
from module_pon.update.service.update_service import ExcelDataService# from crud import excel as crud_excel
# from models.base import SessionLocalupdateController = APIRouter(prefix='/update/update', dependencies=[Depends(LoginService.get_current_user)])@updateController.post("/import", summary="导入Excel数据")
async def import_excel(file: UploadFile = File(...)):if not file.filename.endswith(('.xlsx', '.xls')):raise HTTPException(status_code=400, detail="仅支持Excel文件")try:# 读取Excel文件contents = await file.read()df = pd.read_excel(io.BytesIO(contents))# print(df)# 转换为字典列表data = df.to_dict(orient='records')# print(data)# 保存到数据库async for db in get_db():inserted_count = await ExcelDataService.create_excel_data_bulk(db, data)return ResponseUtil.success(data={"count": inserted_count,"sample_data": data[:3] if len(data) > 3 else data # 返回前3条数据作为示例},msg=f"成功导入{inserted_count}条数据")except Exception as e:logger.error(f"导入Excel失败: {str(e)}")raise HTTPException(status_code=500, detail=f"导入失败: {str(e)}")@updateController.get("/template", summary="下载Excel模板")
async def download_template():# 模板文件路径template_path = "static/templates/excel_template.xlsx"if not os.path.exists(template_path):# 动态生成模板df = pd.DataFrame(columns=["序号", " ", " ", "账 ", " "])df.to_excel(template_path, index=False)return FileResponse(template_path,filename=f"excel_template_{datetime.now().strftime('%Y%m%d')}.xlsx",media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
配置前端
修改update.js
这里主要引入API方法已经写好了,直接导入即可。
import request from '@/utils/request'// 导入Excel
export function importExcel(data) {return request({url: "/update/update/import",method: "post",data,headers: {"Content-Type": "multipart/form-data"}})
}// 下载模板
export function downloadTemplate() {return request({url: "/update/update/template",method: "get",responseType: "blob"})
}
修改index.vue
主要修改的就是路径问题,其他也可以直接复制粘贴。
<template v-slot:header><div><div class="app-container"><el-card><div class="clearfix"><span>PON口数据导入</span></div><el-uploadclass="upload-demo"drag:action="uploadUrl":headers="headers":on-success="handleSuccess":on-error="handleError":before-upload="beforeUpload":show-file-list="false"accept=".xlsx,.xls"><i class="el-icon-upload"></i><div class="el-upload__text">将Excel文件拖到此处,或<em>点击上传</em></div><div class="el-upload__tip" >只能上传xlsx/xls文件,且不超过10MB</div></el-upload><el-button type="primary" style="margin-top: 20px"@click="downloadTemplate"><i class="el-icon-download"></i> 下载模板</el-button></el-card></div></div>
</template><script>
import { importExcel, downloadTemplate } from "@/api/update/update";
import { getToken } from "@/utils/auth"export default {name: "Update",// name: "ExcelImport",data() {return {uploadUrl: process.env.VUE_APP_BASE_API + "/update/update/import",headers: {"Authorization": "Bearer " + getToken()},};},methods: {beforeUpload(file) {const isExcel = /\.(xlsx|xls)$/.test(file.name.toLowerCase())const isLt10M = file.size / 1024 / 1024 < 10if (!isExcel) {this.$message.error("只能上传Excel文件!")return false}if (!isLt10M) {this.$message.error("文件大小不能超过10MB!")return false}return true},handleSuccess(response, file, fileList) {if (response.code === 200) {this.$message.success(response.msg)} else {this.$message.error(response.msg || "导入失败")}},handleError(err, file, fileList) {this.$message.error("上传失败: " + (err.message || "服务器错误"))},async downloadTemplate() {try {const response = await downloadTemplate()const blob = new Blob([response], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" })const link = document.createElement("a")link.href = URL.createObjectURL(blob)link.download = "excel_template.xlsx"link.click()URL.revokeObjectURL(link.href)} catch (error) {this.$message.error("下载模板失败")}},}
};
</script><style scoped>
.upload-demo {text-align: center;
}
</style>
在已制作好的表单中添加字段
在实际开发过程中,我们会经常碰到需求放说还要添加字段的情况,根据这一需要,补充起来其实也不算难。涉及修改的表主要有:xx_do.py,xx_vo.py,xx_service.py,index.vue。
后端配置
比如我要添加的是联系号码这个字段。首先我们需要在数据库中配置这个字段,把这个字段添加进我们的表中,才可是修改代码。
修改xx_do.py
根据之前数据库配置好的字段,在文件中补充字段。
lxhm = Column(String(255), nullable=True, comment='联系号码')
修改xx_vo.py
同样的补充联系号码的接口字段。
lxhm: Optional[str] = Field(default=None, description='联系号码')
修改xx_service.py
在映射字典中补充这个字段的信息。
mapping_dict = {'pDateNo': '更新时间','lxhm': '联系号码',}
配置前端
修改index.vue
根据自己的需求,在最后补充这个字段信息就可以了。
<el-table-column label="产品类型" align="center" prop="prodName" v-if="false"/><el-table-column label="联系号码" width="110" align="center" prop="lxhm" />
data中也要记得补充:
queryParams: {pageNum: 1,pageSize: 10,regionName: null,lxhm : null,},.....reset() {this.form = {lxhm : null,};
补充一个小内容(筛选添加筛选)
修改的地方主要为:
前端筛选样式index.vue
<!-- <el-form-item label="产品类型" prop="prodName"><el-inputv-model="queryParams.prodName"placeholder="请输入产品类型"clearable@keyup.enter.native="handleQuery"/>
后端筛选条件xx_dao.py
query = (select(PonGuzhangUserT2).where(PonGuzhangUserT2.p_date_no == query_object.p_date_no if query_object.p_date_no else True,# PonGuzhangUserT2.cust_name.like(f'%{query_object.cust_name}%') if query_object.cust_name else True,).order_by(PonGuzhangUserT2.p_date_no).distinct())
对前端做权限配置
目前通过多种方法,在后端直接拿到权限编码做筛选配置不是很顺利。这里可以尝试使用一个最差的一个权限赋权的办法,前端自带筛选表单。
},// 异步操作给后端添加除了角色为admin1、admin2以外的用户添加区县查询权限(在vue创建时直接进行一个筛选判断)async created() {const userInfo = await getInfo();if (userInfo && userInfo.user && userInfo.user.dept && userInfo.roles[0] != "admin1" && userInfo.roles[0] != "admin2") {this.queryParams.regionName = userInfo.user.dept.deptName;}this.getList();},methods: {
这样它就会在每次登录前先做一次筛选。