目录
| Assignment 1 | Front-end and Back-end Separation Contacts Programming |
|---|---|
| Course for This Assignment | EE308FZ |
| Assignment Requirements | Front-end and Back-end Separation Contacts Programming |
| Name | Cheng Wang |
| Student ID | MU: 23126167 FZU: 832302103 |
| Objectives of This Assignment | Using web frameworks or other platforms to implement a front-end and back-end separated contacts management system with basic functionalities like adding, modifying, and deleting contacts. |
| Other Reference | Flask Vue |
PSP表格
| 任务 | 子任务 | 描述 | 计划时间(h) | 实际时间(h) |
|---|---|---|---|---|
| 规划 | 需求分析 | 模块划分分析,技术选型(Vue3 + Flask + MySQL) | 2.0 | 1.5 |
| 开发 | 前端分析 | 分模块开发(登录页、首页、通讯录) | 3.0 | 3.0 |
| 登录接口 | 登录相关开发,集成前后端,权限管理 | 3.5 | 3.5 | |
| 功能接口 | 接口开发(id, username, password, name, phone, email)集成 | 2.0 | 1.0 | |
| 前端路由 | 集成 Vue3 + Router 路由开发,组件开发与交互 | 3.5 | 3.5 | |
| 前端功能 | 前端开发,视图交互,组件开发,集成,响应式适配 | 3.0 | 4.5 | |
| 后端功能 | 集成 Flask 开发路由、模型、服务层集成 | 5.5 | 6.5 | |
| 测试接口 | 接口单元测试,集成测试,单元测试,集成测试 | 3.0 | 3.0 | |
| 测试 | 单元测试 | 单元测试开发,后端功能测试 | 2.0 | 1.5 |
| 集成测试 | 集成测试开发,前后端联调测试 | 2.5 | 2.5 | |
| 手动测试 | 手动测试开发,SQL 语句编写与测试 | 2.5 | 2.5 | |
| 部署 | 发布部署 | 集成 Flask 项目打包部署,前端 Vue 打包,环境配置 | 3.0 | 3.0 |
| 数据库部署 | 在云服务器部署 MySQL,数据导入与备份 | 2.0 | 1.5 | |
| 运维测试 | 运维测试开发,性能监控 | 1.5 | 1.5 | |
| 评估 | 文档撰写 | 文档撰写与用户手册 | 2.5 | 2.5 |
| 合计 | - | 总计 | 35.5 | 36.5 |
访问链接
Github仓库地址
前端代码:832302103_contacts_frontend
后端代码:832302103_contacts_backend
1. 功能展示
1.1 登录通讯录系统

1.2 增加联系人

1.3 删除联系人

1.4 修改联系人

1.5 查询联系人

1.6 联系人分组

2. 开发过程
Functional structure diagram

2.1 后端开发
2.1.1 数据库搭建
在这次作业中,我使用了MySQL数据库。我使用的是MAC OS操作系统,首先我需要使用homebrew安装数据库:
brew install mysql
安装之后启动服务:
brew services start mysql
然后配置账号密码即可,此处不再展示。至此,MySQL数据库已经搭建完成。
2.1.2 创建contact表
搭建数据库之后,使用命令行进入数据库进行联系人列表的创建:
mysql -u root -p
输入密码即可进入数据库。然后使用命令行创建联系人列表:
CREATE DATABASE contacts_db
检查是否创建成功:
SHOW DATABASES
终端显示:
+--------------------+
| Database |
+--------------------+
| contacts_db |
| information_schema |
| mysql |
| performance_schema |
| py_test_db |
| sys |
+--------------------+
可以发现,数据库中已经成功创建了联系人表格。
2.1.3 python连接数据库+实现路由
本次作业,我使用python连接数据库,并使用Flask框架实现API路由。Flask是一个轻量级的Web框架,提供基本的路由与请求处理,适合本次作业的开发。
首先,导入必要的包:
import pymysql
from flask import Flask, request, jsonify
from flask_cors import CORS
简要介绍一下这几个包:os用于文件管理,pymsql用于使用python连接MySQL数据库。flask_cors用于允许跨域访问(因为这次作业需要前端与后端分离,因此两者的域名不同,当前端调用后端API时,需要用到该扩展)。
在代码一开始,先声明使用pymysql作为MySQL的驱动,避免SQLAlchemy的兼容问题:
pymysql.install_as_MySQLdb()
接下来,创建数据库连接函数,每次连接数据库时进行调用:
def get_db_connection():
connection = pymysql.connect(
host=DB_HOST,
port=DB_PORT,
user=DB_USER,
password=DB_PASSWORD,
database=DB_NAME,
charset='utf8mb4', # 支持中文和表情符号
cursorclass=pymysql.cursors.DictCursor # 返回字典格式的结果
)
return connection
-
增加联系人
我使用python的Flask框架创建RESTful API接口。首先,我实现了添加联系人的功能:
@app.route('/api/contacts', methods=['POST'])
def add_contact():
name = data['name']
phone = data['phone']
position = data.get('position', '')
group_name = data.get('group_name', '未分组')
cursor.execute("INSERT INTO contact (name, phone, position, group_name) VALUES (%s, %s, %s, %s)",
(name, phone, position, group_name))
conn.commit()
return jsonify({
'message': '联系人添加成功',
'contact': {'id': cursor.lastrowid, 'name': name, 'phone': phone,
'position': position, 'group_name': group_name}
}), 201
这个函数实现了:接收前端发送的数据、连接数据库并插入到SQL表,使用try-except-finally进行异常处理。
-
查询联系人
支持在姓名、电话、职务三个字段中进行模糊搜索:
@app.route('/api/contacts/search', methods=['GET'])
def search_contacts():
keyword = request.args.get('keyword', '').strip()
group_name = request.args.get('group_name')
if not keyword:
return jsonify({'error': '搜索关键词不能为空'}), 400
pattern = f"%{keyword}%"
if group_name:
cursor.execute("""SELECT * FROM contact
WHERE (name LIKE %s OR phone LIKE %s OR position LIKE %s)
AND group_name = %s ORDER BY id DESC""",
(pattern, pattern, pattern, group_name))
else:
cursor.execute("""SELECT * FROM contact
WHERE name LIKE %s OR phone LIKE %s OR position LIKE %s
ORDER BY id DESC""",
(pattern, pattern, pattern))
contacts = cursor.fetchall()
return jsonify({'contacts': contacts, 'count': len(contacts)}), 200
这个函数实现了接收前端的查询要求,编写查询语句,执行并返回结果,同时处理了异常情况。
-
编辑联系人
我的系统可以行内编辑,点击"编辑"按钮后,该行变为可编辑状态:
@app.route('/api/contacts/<int:contact_id>', methods=['PUT'])
def update_contact(contact_id):
# 检查联系人是否存在
cursor.execute("SELECT * FROM contact WHERE id = %s", (contact_id,))
contact = cursor.fetchone()
if not contact:
return jsonify({'error': '联系人不存在'}), 404
# 更新数据
name = data.get('name', contact['name'])
phone = data.get('phone', contact['phone'])
position = data.get('position', contact.get('position', ''))
group_name = data.get('group_name', contact.get('group_name', '未分组'))
cursor.execute("UPDATE contact SET name=%s, phone=%s, position=%s, group_name=%s WHERE id=%s",
(name, phone, position, group_name, contact_id))
conn.commit()
return jsonify({'message': '联系人更新成功', 'contact': {...}}), 200
这个函数实现了接收前端的修改信息,编写修改语句,执行并返回结果,同时处理了异常情况。
-
删除联系人
删除联系人的实现方式与增添联系人类似,这里仅展示不同代码
# 2. 先查询联系人是否存在
check_sql = "SELECT * FROM contact WHERE id = %s"
cursor.execute(check_sql, (contact_id,))
contact = cursor.fetchone()
if not contact:
return jsonify({'error': '联系人不存在'}), 404
# 3. 编写 SQL 删除语句
delete_sql = "DELETE FROM contact WHERE id = %s"
# 4. 执行删除
cursor.execute(delete_sql, (contact_id,))
# 5. 提交事务
connection.commit()
# 6. 返回成功响应
return jsonify({
'message': '联系人删除成功'
}), 200
这个函数实现了接收前端的删除请求,编写删除语句,执行并返回结果,同时处理异常情况。
2.2 前端开发
2.2.1 前端简介
前端三大元素:HTML, JavaScript, CSS
HTML提供网页骨架结构,CSS进行美化,JavaScript实现交互逻辑

-
HTML
HTML通过一系列的标签,来定义文本、图像、链接等。HTML标签是由尖括号包围的关键字。
标签通常成对出现,包括开始标签与结束标签,内容位于这两个标签之间。
除了双标签,也存在单标签。双标签用于有内容的元素,单标签用于没有内容的元素。
HTML的标签包括:六级标签、段落标签、无序列表、有序列表
属性用于定义元素的行为和外观,以及与其他元素的关系
块元素:通常会从新的一行开始,并占据整行的宽度,呈现为一块独立的内容块(<div>, <p>, <h1>...<h6><ul><ol><li><table><form>)
行内元素:在同一行呈现<span><a><strong><em><img><br><input>
div标签:块级标签。创建一个容器块,没有语义,仅用于组织结构。
-
CSS
CSS用于定义网页演示和布局,实现更精准的页面设计
CSS通常由选择器、属性和属性值组成,多个规则可以组合在一起,以便同时应用多个样式
CSS在HTML中有三种导入方式:内联样式、内部样式表(head 中定义)、外部样式表(定义在其它文件中,并在head中导入,允许多个网页使用相同样式)
优先级:内联 > 外部 > 内部
选择器:允许针对特定元素或一组元素定义样式
-
JavaScript
JS被设计用于在网页上实现动态效果,增加用户与网页的交互性
JS在前端中的作用:
客户端脚本:在浏览器中执行,实现动态效果和用户交互
网页开发:与HTML和CSS协同工作,加强动态性与交互性
后端开发:使用Node.js,JS也可以在服务器端运行,实现服务器端应用的开发
2.1.4 Vue3框架
前置:安装Node.js
使用官方脚手架创建Vue工程
进入空文件夹:npm create vue@latest
会自动提供一些配置信息
然后一个框架就被自动创建了:

此时,命令行输入:npm install
会自动下载依赖到node_modules文件夹
public用来放页面图标
src用来放代码文件
index.html是访问入口,一个vue项目都是在这一个单页面中进行各种交互,这也被称为SPA(Single Page Application)
Vue3的核心是通过createApp函数创建一个应用实例(绑定在index.html中),在这个实例中构建各种应用
每个Vue文件就是一个页面上的组件,组件可以嵌套使用
每个vue文件包括三个部分:模版templte 脚本script 样式style
此时命令行输入npm run dev即可运行了,初始页面是这样的:

2.2.2 项目前端开发
-
登录注册页面

我使用了蓝色主题,界面干净简洁
.auth-container {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 50%, #90caf9 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.auth-card {
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
animation: slideUp 0.5s ease;
}
-
主界面

我使用了侧边栏+右侧内容区的布局
.auth-container {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 50%, #90caf9 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.auth-card {
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
animation: slideUp 0.5s ease;
}
-
响应式布局
我使用媒体查询适配了不同屏幕尺寸
@media (max-width: 768px) {
.app-body {
flex-direction: column;
}
.sidebar {
width: 100%;
border-right: none;
border-bottom: 1px solid #e4e7ed;
}
.contact-form {
grid-template-columns: 1fr;
}
}
3. 拓展功能
3.1 登录功能

3.1.1 前端实现
const handleLogin = async () => {
if (!loginForm.value.username || !loginForm.value.password) {
showToast('请填写完整信息', 'error')
return
}
try {
const data = await api.login(loginForm.value)
isAuthenticated.value = true
currentUser.value = data.username
showToast('登录成功!欢迎回来!')
fetchGroups()
fetchContacts()
} catch (err) {
showToast(err.message, 'error')
}
}
3.1.2 后端实现
@app.route('/api/login', methods=['POST'])
def login():
username = data['username']
password = data['password']
cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
user = cursor.fetchone()
# 验证密码
if not user or not check_password_hash(user['password_hash'], password):
return jsonify({'error': '用户名或密码错误'}), 401
# 设置Session
session['user_id'] = user['id']
session['username'] = user['username']
return jsonify({'message': '登录成功!', 'username': user['username']}), 200
3.1.3 技术要点
- 使用
check_password_hash验证密码 - Flask Session机制保持登录状态
- 前端使用
withCredentials: true携带Cookie
3.2 联系人分组功能

3.2.1 前端实现
<div v-if="showAddGroupModal" class="modal-overlay">
<div class="modal-content">
<div class="modal-header">
<h3>添加新分组</h3>
<button @click="showAddGroupModal = false">×</button>
</div>
<div class="modal-body">
<input v-model="newGroupName" placeholder="请输入分组名称" />
</div>
<div class="modal-footer">
<button @click="showAddGroupModal = false" class="btn btn-cancel">取消</button>
<button @click="addGroup" class="btn btn-primary">添加</button>
</div>
</div>
</div>
3.2.2 后端数据库列表
@app.route('/api/groups', methods=['GET'])
def get_all_groups():
cursor.execute("""
SELECT g.id, g.name, COUNT(c.id) as count
FROM contact_group g
LEFT JOIN contact c ON g.name = c.group_name
GROUP BY g.id, g.name
ORDER BY g.id ASC
""")
groups = cursor.fetchall()
return jsonify({'groups': groups}), 200
3.2.3 技术要点
- 使用
LEFT JOIN连接分组表和联系人表 - 使用
COUNT()聚合函数统计数量 - 使用
GROUP BY分组统计
4. 个人学习收获
作为一名学生,在开发这个通讯录管理系统项目时,我一开始遇到了一些困难。之前仅接触过基础教程,现在需要构建一个完整的前后端应用,这让我认识到学习编程需要有条理的规划。因此,我选择循序渐进,先从后端开始,确保数据基础稳固后再转向界面开发。
首先,我决定进行后端Flask的开发。这一部分一开始比较困难,因为我缺乏后端经验。但我从简单API入手,先定义了登录和注册的端点,使用`@app.route`来路由请求,并通过`generate_password_hash`处理密码加密,这让我理解了数据安全的必要性。然后,我学习了MySQL数据库的操作。创建表结构(如users、contact和contact_group表)和执行查询语句时,最初遇到了一些语法错误,但通过反复测试PyMySQL连接,我成功插入了第一条联系人数据。分组功能的实现例如使用JOIN查询来计算分组计数进一步加深了我对表的理解。通过Postman测试API端点,我逐步验证了后端功能,确保后端功能完整。
接下来,我转向Vue.js的前端部分。理解模板语法和组件的交互比较困难,例如使用`v-if`来切换登录界面和主应用界面。但根据搭建的后端接口,我逐步编写代码。在实现登录和注册表单时,我学习了`v-model`来绑定用户输入,实现实时响应,这让我对界面的动态性有了直观认识。随后,我开发了添加联系人的表单和联系人列表,包括搜索、编辑和删除功能。过程中,CSS样式的调整让我学会了如何优化用户体验,每完成一个子模块,都能看到页面的逐步完善,这增强了我的信心。
最后,集成前后端是整个项目的关键,我也收获最多。我使用axios从前端发送HTTP请求到后端API,例如在添加联系人时传递数据并更新列表。过程中,遇到了CORS跨域和会话管理的问题,我通过网络搜索和逐步调试来解决,这培养了我问题排查的能力。项目最终运行顺利时,看到联系人能被搜索、编辑和分组管理,我感到很激动。
通过这个项目,我不仅掌握了Flask、MySQL和Vue.js的基本应用,还学会了项目开发的方法:从后端API起步,逐步集成前端。未来,我打算利用这次项目学到的知识,更好地完成学期项目。
815

被折叠的 条评论
为什么被折叠?



