164
社区成员
发帖
与我相关
我的任务
分享| 本次作业所属课程 | EE308FZ |
|---|---|
| 作业要求 | link |
| 这项任务的目标 | 前后端分离网页开发 |
| 其他参考资料 | https://peps.python.org/pep-0008/ https://google.github.io/styleguide/ |
| 前端链接 | https://github.com/CyberTrash-star/832301308_contacts_frontend |
| 后端链接 | https://github.com/CyberTrash-star/832301308_contacts_backend |
| 任务 | 预估时间(小时) | 实际时间(小时) |
|---|---|---|
| 需求分析 | 1 | 1 |
| 前端页面开发 | 12 | 15 |
| 后端开发 | 10 | 13 |
| 前后端联调 | 3 | 2 |
| 服务器部署 | 7 | 9 |
| 文档编写 | 3 | 3 |
| 总计 | 36 | 43 |




| 模块 | 技术选型 |
|---|---|
| 框架 | Flask |
| ORM | Flask-SQLAlchemy |
| 数据库 | MySQL |
| 页面结构 | HTML5 |
| 样式 | CSS3 + Bootstrap 5 |
| 交互 | jQuery |
832301308_contacts_backend/src/
├── app.py # 应用入口
├── config.py # 全局配置
├── exts.py # 扩展实例化
├── models.py # 数据模型
├── controller/ # 接口控制器
│ └── contacts.py # 联系人接口实现
├── gunicorn_conf.py # 部署配置
832301308_contacts_frontend/src/
├── index.html # 前端界面
// 加载联系人数据
function loadContacts() {
const keyword = $('#searchInput').val().trim();
$.get(
'http://47.112.217.152:5000/api/contacts',
{ keyword: keyword },
function(response) {
response.code === 200 && renderTable(response.data);
}
).fail(() => alert('网络错误'));
}
// 渲染表格
function renderTable(contacts) {
const tableBody = $('#contactTableBody').empty();
contacts.forEach((contact, index) => {
tableBody.append(`
<tr>
<td><input type="checkbox" class="contact-checkbox" data-id="${contact.id}"></td>
<td>${index + 1}</td>
<td>${contact.username}</td>
<td>${contact.phone_number}</td>
<td>${contact.address}</td>
</tr>
`);
});
}
let currentContactId = null; // 标记当前操作ID
// 保存联系人(复用添加/修改逻辑)
function saveContact() {
const contactData = {
username: $('#username').val().trim(),
phone_number: $('#phone').val().trim(),
address: $('#address').val().trim()
};
// 表单验证
if (!contactData.username || !contactData.phone_number || !contactData.address) {
return alert('请填写所有字段');
}
// 区分操作类型
const [url, method] = currentContactId
? [`http://47.112.217.152:5000/api/contacts/${currentContactId}`, 'PUT']
: ['http://47.112.217.152:5000/api/contacts', 'POST'];
$.ajax({
url: url,
type: method,
contentType: 'application/json',
data: JSON.stringify(contactData),
success: (response) => {
if ([200, 201].includes(response.code)) {
alert(response.message);
$('#contactModal').modal('hide');
loadContacts(); // 刷新数据
}
}
});
}
function deleteContact() {
const checked = $('.contact-checkbox:checked');
if (!checked.length) return alert('请选择联系人');
if (confirm('确定删除?')) {
$.ajax({
url: `http://47.112.217.152:5000/api/contacts/${checked.first().data('id')}`,
type: 'DELETE',
success: (response) => {
response.code === 200 && (alert(response.message), loadContacts());
}
});
}
}
from datetime import datetime
from exts import db
class UserModel(db.Model):
__tablename__ = 'user' # 对应数据库表名
id = db.Column(db.Integer, primary_key=True, autoincrement=True) # 主键自增
username = db.Column(db.String(100), nullable=False, comment='联系人姓名') # 必填
phone_number = db.Column(db.String(20), nullable=False, unique=True, comment='联系电话') # 唯一必填
address = db.Column(db.String(200), nullable=False, comment='联系地址') # 必填
join_time = db.Column(db.DateTime, default=datetime.now, comment='创建时间') # 自动填充
def to_dict(self):
"""模型转字典,适配JSON响应"""
return {
'id': self.id,
'username': self.username,
'phone_number': self.phone_number,
'address': self.address,
'join_time': self.join_time.strftime('%Y-%m-%d %H:%M:%S')
}
from flask import Blueprint, request, jsonify
from models import UserModel
from exts import db
bp = Blueprint('contacts', __name__)
# 添加联系人
@bp.route('/contacts', methods=['POST'])
def add_contact():
data = request.get_json()
if not all([data.get('username'), data.get('phone_number'), data.get('address')]):
return jsonify({'code': 400, 'message': '请填写所有字段'}), 400
if UserModel.query.filter_by(phone_number=data.get('phone_number')).first():
return jsonify({'code': 400, 'message': '手机号已存在'}), 400
new_contact = UserModel(username=data.get('username'), phone_number=data.get('phone_number'), address=data.get('address'))
db.session.add(new_contact)
db.session.commit()
return jsonify({'code': 201, 'message': '添加成功', 'data': new_contact.to_dict()}), 201
# 更新联系人
@bp.route('/contacts/<int:id>', methods=['PUT'])
def update_contact(id):
contact = UserModel.query.get(id)
if not contact:
return jsonify({'code': 404, 'message': '联系人不存在'}), 404
data = request.get_json()
if data.get('phone_number') != contact.phone_number and UserModel.query.filter_by(phone_number=data.get('phone_number')).first():
return jsonify({'code': 400, 'message': '手机号已存在'}), 400
contact.username = data.get('username')
contact.phone_number = data.get('phone_number')
contact.address = data.get('address')
db.session.commit()
return jsonify({'code': 200, 'message': '更新成功', 'data': contact.to_dict()})
# 删除联系人
@bp.route('/contacts/<int:id>', methods=['DELETE'])
def delete_contact(id):
contact = UserModel.query.get(id)
if not contact:
return jsonify({'code': 404, 'message': '联系人不存在'}), 404
db.session.delete(contact)
db.session.commit()
return jsonify({'code': 200, 'message': '删除成功'})
在接触这个项目前,我虽然学过 Flask、jQuery 等技术,但大多停留在 “跟着教程敲代码” 的阶段。所以我希望通过这个联系人管理系统,实现两个目标:一是把零散的知识点串联起来,形成完整的开发思维;二是直面真实开发中的问题(比如跨域、数据验证、联调 bug),提升解决实际问题的能力。
开发中我发现,只靠后端验证数据远远不够。比如用户没填完表单就提交,要等后端返回错误才提示。于是在前端添加了表单验证:提交前检查姓名、电话、地址是否为空,及时给出提示。
另外,删除操作容易误触,所以我加了确认对话框;请求失败时(比如网络错误、后端服务未启动),会给出明确的错误提示,而不是让用户面对空白页面不知所措。这些细节虽然简单,却让系统变得更 “好用”。
后端:熟练掌握 Flask 框架的核心用法(工厂模式、蓝图、扩展),理解 SQLAlchemy ORM 的底层逻辑,能独立设计数据模型和规范接口;
前端:精通 jQuery 的 DOM 操作和 AJAX 请求,学会用 Bootstrap 快速搭建响应式界面,掌握表单验证、动态渲染等实用技巧;
我计划在后续的补充开发中完善联系人查询功能(目前只实现了前端界面设计和接口预留),这次的网站构建对我来说是一次 “从零到一” 的完整实践。