大家好~我是
米洛!
我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的教程,希望大家多多支持。
欢迎关注我的公众号米洛的测开日记,获取最新文章教程!
回顾
上一节咱们编写好了操作日志相关功能,这一节我们就来聊聊怎么展示它,展示操作日志是一部分,个人主页也是另一部分。
当我们脑海里没有类似的蓝图时,可以借鉴下其他web的效果。比如掘金(注意这不是硬广):

可以看到他大致分成了3块:
- 用户信息,你是谁,你的简介,联系方式等
- 个人动态
- 个人成就以及粉丝等数据
考虑到成就和粉丝这个概念我们内部可能不太实用,所以我们以个人资料和个人动态为主题,构建我们的个人首页。
参考一家肯定不够的,我们看看github:

这个热力图和动态是我们想要展示的。所以我们采用左侧个人信息+右侧热力图/动态的方式展示个人页。
先来看看最终效果:

编写获取操作记录的接口
- 根据时间获取用户的操作数据(对应热力图)
修改PityOperationDao.py
from sqlalchemy import func, selectfrom app.crud import Mapperfrom app.models import async_sessionfrom app.models.operation_log import PityOperationLogfrom app.utils.decorator import daofrom app.utils.logger import Log@dao(PityOperationLog, Log("PityOperationDao"))class PityOperationDao(Mapper):@classmethodasync def count_user_activities(cls, user_id, start_time: str, end_time: str):"""根据开始/结束时间 获取用户的活动日历(操作记录的数量):param user_id::param start_time::param end_time::return:"""async with async_session() as session:async with session.begin():format_date = func.date_format(PityOperationLog.operate_time, "%Y-%m-%d")sql = select(format_date, func.count(PityOperationLog.id)).where(PityOperationLog.operate_time.between(start_time, end_time),PityOperationLog.user_id == user_id) \.group_by(format_date).order_by(format_date)data = await session.execute(sql)return data.all()
这里的sqlalchemy语句比较复杂,注意2个点即可:
- func
这里的func是时间转换的func,我们操作记录存放的是具体的日期+时间,但我们查询用户操作一般是按日期来,所以我们需要转换下:
func.date_format干的就是这个事情,为了group_by。
这个sql就是筛选出user_id等于要查询用户,并且时间在指定日期之内的数据,通过group_by拿到每一天的操作次数,最后根据日期排序后返回。
接着创建app/router/operation/operation_log.py
from fastapi import APIRouter, Dependsfrom sqlalchemy import descfrom app.crud.operation.PityOperationDao import PityOperationDaofrom app.handler.fatcory import PityResponsefrom app.models.operation_log import PityOperationLogfrom app.routers import Permissionrouter = APIRouter(prefix="/operation")# 获取用户操作记录热力图以及参与的项目数量@router.get("/count")async def list_user_activities(user_id: int, start_time: str, end_time: str, _=Depends(Permission())):try:records = await PityOperationDao.count_user_activities(user_id, start_time, end_time)ans = list()for r in records:# 解包日期和数量date, count = rans.append(dict(date=date, count=count))return PityResponse.success(ans)except Exception as e:return PityResponse.failed(e)
获取到数据后,把日期和date组合成json数组。
- 编写获取用户操作日志详情的接口
# 获取用户操作记录@router.get("/list")async def list_user_operation(start_time: str, end_time: str, user_id: int, tag: str = None, _=Depends(Permission())):try:records = await PityOperationDao.list_record(user_id=user_id, tag=tag, condition=[PityOperationLog.operate_time.between(start_time, end_time)], desc=[desc(PityOperationLog.operate_time)])return PityResponse.records(records)except Exception as e:return PityResponse.failed(e)
调用Mapper自带的list_record方法,根据条件筛选出对应的数据并返回。这把在PityResponse里封装了一个records方法:
@staticmethoddef records(data: list, code=0, msg="操作成功"):return dict(code=code, msg=msg, data=PityResponse.model_to_list(data))
这样就不用每次都model_to_list了。
前端页面
- 在route.js加入member路由,不在菜单里展示

- 接着是编写UserInfo.jsx
代码我就不贴了,github都有详细的,有兴趣的可以拉下来看看。
大概也就是根据接口的返回,生成对应的动态和个人资料页面。
封装个人用户组件
import React from 'react';import {Avatar} from "antd";import styles from "@/components/GlobalHeader/index.less";import {CONFIG} from "@/consts/config";import {Tooltip} from "antd";export default ({user, size = 24, marginLeft = 4}) => {if (user === undefined) {return '加载中...'}// 是否和左边有距离,有的话则为2return (<><Avatar size={size} className={styles.avatar}src={user.avatar || `${CONFIG.AVATAR_URL}${user.name}`} alt="avatar"/><Tooltip title="点击可查看用户资料"><a style={{marginLeft: marginLeft}} href={`/#/member/${user.id}`} target="_blank"rel="noreferrer">{user.name}</a></Tooltip></>)}
这个组件接受用户信息,和头像尺寸。为啥要这个组件呢?
因为我们的用户资料页面没有提供入口,也就是说菜单里找不到。再者,我们需要把用户名改为头像+用户名,这样看起来更nice。
看看实战效果:

点击链接就可以到个人资料了。
今天的内容就说到这里了,希望对大家有帮助。
