前景回顾
上一篇文章,主要阐述了应用前端工程的搭建与部分页面开发
本文简单介绍一下一期剩余的页面(体重记录页)开发,着重阐述后端部分的必要设计
本文涉及内容
- 体重记录页的开发
- 初始化后端Node+TypeScript项目
- 数据库设计
- 接口设计
- 云数据库初始化
- 数据库操作方法封装
体重记录页开发
页面整体上由导航
,最近一次的记录
,对比描述
,历史记录
,添加数据弹窗
等5部分组成
导航
直接使用vant-nav-bar
左按钮返回上一页,右按钮(icon) 唤起添加添加成员的弹窗: van-dialog
<van-nav-bar title="体重记录" @click-left="handleBack" @click-right="handleAddPeople" left-text="返回" left-arrow > <template #right> <van-icon name="plus" size="18" /> </template> </van-nav-bar>
效果
网络异常,图片无法展示
|
引入Dialog组件注意: 由于Dialog
支持直接当作方法使用Dialog(options)
,再当作组件注册时与其它组件不太一样:
src/utils/vantUI.ts
import { Button, Dialog } from 'vant' const conponents = [Button] export default function mountVantUI(app: App<Element>) { conponents.forEach((c) => { app.component(c.name, c) }) // 特别对待 app.component(Dialog.Component.name, Dialog.Component) }
最近一次的记录
展示一下时间与体重即可
<h2 class="current-time">2021-06-15 12:00:00</h2> <h1 class="current-weight">48.12<span>公斤</span></h1>
效果
网络异常,图片无法展示
|
对比描述
包含最新的一次记录与
- 上一次比较
- 与今天第一次比较
- 与本月第一次比较
展示间隔的时间,并反应上升/下降的体重
页面结构
<p class="rank" v-for="(t, idx) in overviewData" :key="idx"> {{ t.text }} <span :class="t.symbol"></span> <span class="res">{{ t.res }}</span> </p> <!-- 渲染结果示例 --> <p class="rank"> 与上一次比较(5小时前) <span class="add"></span> <span class="res">5 公斤</span> </p>
效果
网络异常,图片无法展示
|
其中↑与↓的表示采用伪元素::after
表示
.dec::after { content: '🠐'; color: green; transform: rotate(-90deg); } .inc::after { content: '🠖'; color: #ff6034; transform: rotate(-90deg); }
const ONE_SECONDS = 1000 const ONE_MINUTE = ONE_SECONDS * 60 const ONE_HOUR = ONE_MINUTE * 60 const ONE_DAY = ONE_HOUR * 24 function getTimeDiffDes(d1: Date, d2: Date) { const diff = d1.getTime() - d2.getTime() // 天 if (diff / ONE_DAY >= 1) { return `${Math.round(diff / ONE_DAY)}天前` } // 小时 if (diff / ONE_HOUR >= 1) { return `${Math.round(diff / ONE_HOUR)}小时前` } // 分钟 if (diff / ONE_MINUTE >= 1) { return `${Math.round(diff / ONE_MINUTE)}分钟前` } // 秒 return `${Math.round(diff / ONE_SECONDS)}秒前` }
历史数据展示
直接套用Vant的 van-swipe-cell与 van-cell
<van-swipe-cell v-for="(t, idx) in weights" :key="idx"> <van-cell :border="false" :title="formatDate(t.date)"> {{ t.weight }} </van-cell> <template #right> <van-button @click="hadnleDeleteWeight(idx)" square type="danger" text="删除" /> </template> </van-swipe-cell>
效果
网络异常,图片无法展示
|
页面最终效果
网络异常,图片无法展示
|
初始化后端工程
直接使用搭建的ATQQ/node-server模板初始化项目
网络异常,图片无法展示
|
模板工程简介
网络异常,图片无法展示
|
数据库设计
使用: 腾讯云开发CloudBase提供的云数据库 (文档型数据库)
这样从头到尾都不需要买云服务器
相关需求简单回顾
- 短信验证码登录
- 支持同时记录多个人的数据
- 每个数据包含
日期
与体重
两部分内容
简单捋一下思路,使用三张表用户表(user)
,成员表(family)
,记录表(record)
,具体设计如下
用户表-user
字段 | 类型 | 描述 |
userId | String | 唯一标识 |
phone | String | 手机号 |
joinTime | Date | 注册时间 |
成员表-family
字段 | 类型 | 描述 |
familyId | String | 唯一标识 |
userId | String | 关联创建用户 |
name | String | 成员名称 |
记录表-record
字段 | 类型 | 描述 |
recordId | String | 唯一标识 |
familyId | String | 关联成员 |
userId | String | 关联创建用户 |
weight | Number | 体重 |
date | Date | 日期 |
初始化云数据库
服务端使用Node.js开发,使用云开发提供的Node SDK初始化集合(表)
安装依赖
yarn add @cloudbase/node-sdk
初始化集合
const cloudbase = require('@cloudbase/node-sdk') const app = cloudbase.init({ secretId:process.env.secretId, secretKey:process.env.secretKey, env:process.env.envId }) const db = app.database(); db.createCollection('user') db.createCollection('family') db.createCollection('record')
可以在CloudBase-数据库面板看到结果:
网络异常,图片无法展示
|
接口设计
按数据库对接口进行分类,这里只阐述请求方法与路径
使用Eolinker管理与测试接口:此处查看完整接口设计
User
方法 | 路径 | 描述 |
POST | /user/login | 用户登录 |
GET | /user/code | 获取登录验证码 |
Family
方法 | 路径 | 描述 |
POST | /family/add | 添加成员 |
Record
方法 | 路径 | 描述 |
POST | /record/:familyId | 添加记录 |
DELETE | /record/:recordId | 删除记录 |
后端开发
数据库操作方法封装
根据文档云开发 CloudBase > 开发指南 > 数据库 编写
JS版本
const cloudbase = require('@cloudbase/node-sdk') const app = cloudbase.init({ secretId:process.env.secretId, secretKey:process.env.secretKey, env:process.env.envId }) const db = app.database(); function insertDocument(collection, data) { return db.collection(collection).add(data) } function deleteDocument(collection, query) { return db.collection(collection).where(query).remove() } function findDocument(collection, query) { return db.collection(collection).where(query).get() } function updateDocument(collection, query, data) { return db.collection(collection).where(query).update(data) }
TS版本
import cloudbase from '@cloudbase/node-sdk' const app = cloudbase.init({ secretId: process.env.secretId, secretKey: process.env.secretKey, env: process.env.envId }) export const db = app.database() export function insertDocument<T>(collection: string, data: T | T[]) { return db.collection(collection).add(data) } export function deleteDocument(collection: string, query: any) { return db.collection(collection).where(query).remove() } export function findDocument(collection: string, query: any) { return db.collection(collection).where(query).get() } export function updateDocument<T>(collection: string, query: any, data:T) { return db.collection(collection).where(query).update(data) }
未完待续
篇幅有限,下一章节再介绍后端部分的详细实现与前后端对接(/(ㄒoㄒ)/~~,主要还是没码完)