基于html+css+javascript 使用flask
这个作业属于哪个课程 | 软件工程 |
---|---|
这个作业要求在那里 | 作业要求 |
课程目标 | 将计算器实现前后端分离 |
其他参考文献 | 无 |
git仓库链接和代码规范链接
PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
•Planning | •计划 | 50 | 60 |
• Estimate | • 估计这个任务需要多少时间 | 300 | 370 |
Development | 开发 | 60 | 70 |
• Analysis | • 需求分析 (包括学习新技术) | 120 | 200 |
• Design Spec | • 生成设计文档 | 20 | 30 |
• Design Review | • 设计复审 | 10 | 40 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
• Design | • 具体设计 | 20 | 30 |
• Coding | • 具体编码 | 100 | 200 |
• Code Review | • 代码复审 | 10 | 5 |
• Test | • 测试(自我测试,修改代码,提交修改) | 50 | 60 |
Reporting | 报告 | 60 | 60 |
• Test Repor | • 测试报告 | 50 | 20 |
• Size Measurement | • 计算工作量 | 30 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 20 | 30 |
合计 | 970 | 1230 |
成品展示
基本运算
计算器基础计算
科学运算
计算器科学运算
清零回退
计算器清零回退
查看历史记录
计算器查看历史记录
捕捉异常
计算器捕捉异常
设计实现过程
设计思想
这个计算器的设计思想基于MVC(模型-视图-控制器)架构模式,将应用程序分为三个互相关联的部分。
- 模型(Model):
数据存储: 使用SQLite数据库和SQLAlchemy ORM存储计算结果。每次计算都会存储,以便以后可以访问。
数据结构: 设计了一个简单的数据库模型,包含表达式和其计算结果。 - 视图(View):
用户界面: 通过HTML和CSS创建了用户界面,包括数字、操作符和其他功能的按钮。
输出显示: 使用一个输出框来显示用户的输入和计算结果。 - 控制器(Controller):
逻辑处理: JavaScript和jQuery被用于处理用户的输入,管理和执行计算。
与后端通信: 通过AJAX与后端交互,获取计算结果或最新的计算历史。
设计思想:
- 模块化:
代码被模块化,使其易于理解和维护。HTML负责结构,CSS负责样式,JavaScript负责功能和逻辑。 - 交互性:
通过JavaScript和jQuery,实现了与用户的动态交互。用户可以实时地输入、修改表达式,并得到实时的反馈。
扩展性:
由于采用了MVC架构,所以可以在不影响其他部分的情况下修改或添加新功能。 - 安全性:
虽然在后端使用了eval()函数,但设计时也考虑了安全性。可以进一步优化以减少潜在风险。 - 用户友好:
设计了直观且用户友好的界面,提供了清晰的用户指导,易于使用。
前端
HTML:
创建了计算器的布局,包括输出框和各种按钮(数字,操作符,特殊符号等)。
CSS:
用于美化计算器的界面,例如设置边框、颜色、阴影等。
JavaScript 和 jQuery:
事件处理: 为计算器的每个按钮添加点击事件处理函数。
操作队列 (operationQueue): 用于存储用户输入的数字和操作符。
AJAX 请求:
当用户点击“=”按钮时,一个POST请求会发送到后端,请求计算表达式的结果。
当用户点击“ans”按钮时,一个GET请求会发送到后端,请求历史计算结果。
后端
数据库模型(SQLite 和 SQLAlchemy):
一个简单的数据库模型被创建,用于存储计算表达式和它们的结果。
路由和视图函数:
‘/’ (首页): 返回包含计算器界面的HTML页面。
‘/latest’ (获取最新结果): 返回数据库中的最新计算结果。
‘/calculate’ (计算表达式): 计算一个数学表达式的结果,并将其和表达式存储在数据库中
前后端通信
计算器应用的前后端通信主要通过HTTP请求实现,具体包括如下几个步骤:
- 用户输入和计算请求(前端):
用户在前端的HTML页面中输入数学表达式,并点击等于(=)按钮。
JavaScript(JS)捕获用户的输入,并处理这些输入,准备发送到后端。 - 发送请求到后端(前端->后端):
JS使用AJAX(异步JavaScript和XML)发送POST请求到后端。这个请求包含用户输入的数学表达式。
请求是发送到/calculate端点的,由Flask(后端框架)处理。 - 后端处理(后端):
Flask接收到POST请求和用户输入的数学表达式后,进行处理和计算。
计算的结果或任何错误信息将作为响应返回到前端。 - 接收和处理后端的响应(前端):
前端的JS接收到后端的响应,根据响应内容更新前端界面。
如果计算成功,显示计算结果;如果有错误,显示错误消息。 - 获取最新的计算结果(前端->后端):
用户还可以通过点击ans按钮获取最新的计算结果。
点击ans按钮时,JS会发送一个GET请求到/latest端点。
后端处理这个请求,查询数据库并返回最新的计算结果。 - 显示最新的计算结果(前端):
前端接收到包含最新计算结果的响应,并在界面上显示。
数据库存储
- 数据库和表的设置(Initialization):
在 Flask 应用中配置数据库的 URI。
定义一个 SQLAlchemy 模型 CalculationResult,它包含了表达式和结果的列。
当应用启动时,如果还没有创建数据库和表,将会自动创建。 - 执行计算(Calculation):
用户在前端输入一个数学表达式并请求计算。
后端接收这个请求,计算表达式,并准备存储结果。 - 存储计算结果(Storing the Results):
创建一个新的 CalculationResult 对象,用于存储表达式和它的计算结果。
将这个新对象添加到数据库会话中,并提交会话。
这样,新的计算结果就被保存到数据库中了。 - 查询历史计算结果(Querying the Latest Result):
当用户请求历史计算结果时,应用会查询数据库,数据库按照 ID 降序排序结果,并返回第一个结果,即最新的计算结果。 - 返回结果到前端(Returning the Result to the Frontend):
应用将查询到的最新计算结果返回到前端,前端再显示给用户。
关键代码
前端(HTML + JavaScript)
- HTML 结构:
为每个按钮创建 HTML 元素,并分配特定的 CSS 类和 onclick 事件处理程序。
用于显示当前表达式的输出文本框。
<button class="button number-button" onclick="appendNumber('1')">1</button>
<button class="button operation-button" onclick="calculate()">=</button>
- JavaScript 函数:
appendNumber、appendOperation 等函数用于处理用户输入。
calculate 函数用于向后端发送计算请求。
appendLatestResult 函数用于从后端获取并显示最新的计算结果。
function appendNumber(number) {
let lastElement = operationQueue[operationQueue.length - 1];
if (!isNaN(lastElement) || lastElement === '.') {
operationQueue[operationQueue.length - 1] += number;
} else {
operationQueue.push(number);
}
updateDisplay();
}
function calculate() {
let calculation = operationQueue.join('');
$.post("/calculate", { expression: calculation }, function(data) {
if (data.error) {
alert(data.error);
} else {
operationQueue = [data.result.toString()];
updateDisplay();
}
});
}
async function appendLatestResult() {
try {
let response = await fetch('/latest');
let data = await response.json();
if (data.result !== undefined) {
operationQueue.push(data.result.toString()); // 修改这里
updateDisplay();
} else {
alert("No history available");
}
} catch (error) {
console.error("Error while fetching the latest result:", error);
}
}
}
后端(Python + Flask)
3. 数据库模型:
定义 CalculationResult 模型,用于在数据库中存储计算结果。
class CalculationResult(db.Model):
id = db.Column(db.Integer, primary_key=True)
expression = db.Column(db.String(200), nullable=False)
result = db.Column(db.Float, nullable=False)
- 路由和控制器:
@app.route('/calculate', methods=['POST']) 定义了处理计算请求的路由。
@app.route('/latest', methods=['GET']) 定义了返回最新计算结果的路由。
这个路由处理获取最新计算结果的请求。
数据库查询:CalculationResult.query.order_by(CalculationResult.id.desc()).first() 从数据库中检索最新(最后)的计算结果。
返回值: 如果数据库中有计算结果,它将返回最新的计算结果;如果没有,它将返回一个错误消息。
app.route('/latest', methods=['GET'])
def get_latest_result():
latest_result = CalculationResult.query.order_by(CalculationResult.id.desc()).first()
if latest_result:
return jsonify({"result": latest_result.result})
else:
return jsonify({"error": "No history available"})
这个路由处理计算表达式的请求。
获取表达式: 使用 request.form.get(‘expression’) 从 POST 请求中获取用户输入的表达式。
处理表达式: 代码替换了 “Math.sin”, “Math.cos”, “Math.tan”, 和 “Math.log” 为 Python 的 math 模块对应的函数,以便可以正确计算。
执行计算: 使用 Python 的 eval() 函数执行计算。请注意,使用 eval() 函数有安全风险。
保存结果: 将计算结果保存到数据库中。
返回值: 如果计算成功,它将返回计算结果;如果有错误(例如,表达式不合法),它将返回一个错误消息。
@app.route('/calculate', methods=['POST'])
def calculate_expression():
try:
expression = request.form.get('expression')
# Map the incoming expression to use Python's math module functions
expression = expression.replace("Math.sin", "math.sin")
expression = expression.replace("Math.cos", "math.cos")
expression = expression.replace("Math.tan", "math.tan")
expression = expression.replace("Math.log", "math.log")
# Caution: eval() is dangerous
result = eval(expression)
# Save the result to the database
calculation = CalculationResult(expression=expression, result=result)
db.session.add(calculation)
db.session.commit()
return jsonify({"result": result})
except Exception as e:
return jsonify({"error": str(e)})
心路历程和收获:
学习和探索: 在实现过程中,我学习了如何使用HTML和CSS来布局和样式化页面,了解并实践了JavaScript在浏览器中的应用,如操作DOM,事件处理等。学习了如何使用Flask框架构建后端服务,处理HTTP请求,如何使用Flask配置路由和编写处理请求的视图函数。
困难与挫折: 在处理安全性和数据库操作时,遇到了一些困难,但通过不断学习和尝试,最终找到了解决方法。
通过这个项目,我不仅实践了前后端开发的知识,也学到了很多项目管理和问题解决的方法,是一次非常宝贵的经验。