目录
这个作业属于哪个课程 | 软件工程 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业的目标 | 实现前后端交互式科学计算器和利率计算器 |
作业描述
科学计算器功能要求
功能1: 实现加减乘除、取余、括号运算,确保运算顺序正确,计算结果正确,并将输入的表达式和结果存储到后端数据库中。
功能2: 提供清零和回退功能,以便用户可以方便地编辑输入的表达式。
功能3: 实现非法输入的检测与提示,确保用户输入的表达式符合数学运算规则。
功能4: 提供一个 “ans” 按钮,允许用户返回上一个计算结果。同时,显示历史记录,其中包括最后十个输入的表达式和相应的计算结果。历史记录应该从后端数据库中读取,不应使用缓存。
附加功能1: 支持计算根号、三角函数、log、科学计数法、微积分等附加功能,以提供更多数学计算选项。
附加功能2: 设计一个页面原型,包括用户界面和交互元素。
附加功能3: 项目部署。
利率计算器功能要求
功能1: 实现利率计算器,其中利率表被存储在数据库中。通过两个输入框,分别从前端读取用户的输入数据,进行计算并返回结果。输入为:
- 存款/贷款金额
- 存款/贷款时长(单位:年)
输出计算后的存款或贷款利息。在计算利率时,必须从数据库获取相应的利率,而不能在前端的某个变量中存储这个利率表。
数据库存储的利率表如下图所示:
-
存款利率
活期存款 0.5 整存整取定期存款 - 三个月 2.85 半年 3.05 一年 3.25 二年 4.15 三年 4.75 五年 5.25 -
贷款利率
各项贷款 - 六个月 5.85 一年 6.31 一至三年 6.40 三至五年 6.65 五年 6.80
扩展功能: 在前端页面中对相应的信息进行修改,实现对数据库表中数值的更新操作。输入需符合以下要求:
- 存款/贷款
- 时长(单位:年)
- 修改后的利率
最终在数据库的表中相应位置的数据更新。
Gitcode仓库地址
PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 5 | 3 |
• Estimate | • 估计这个任务需要多少时间 | 5 | 3 |
Development | 开发 | 1340 | 2020 |
• Analysis | • 需求分析 (包括学习新技术) | 120 | 200 |
• Design Spec | • 生成设计文档 | 20 | 15 |
• Design Review | • 设计复审 | 15 | 10 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 15 | 10 |
• Design | • 具体设计 | 30 | 45 |
• Coding | • 具体编码 | 900 | 1560 |
• Code Review | • 代码复审 | 60 | 60 |
• Test | • 测试(自我测试,修改代码,提交修改) | 180 | 120 |
Reporting | 报告 | 30 | 40 |
• Test Repor | • 测试报告 | 10 | 15 |
• Size Measurement | • 计算工作量 | 10 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 10 | 15 |
合计 | 1375 | 2063 |
成品功能展示
科学计算器
1. 加减乘除
2.取余
3.括号运算
4.清零和回退
5.非法输入的检测和提示
设置多种非法输入的提示,在处理“++”,“/0"等错误表达式上方会有弹窗弹出报错,详细能够检测的错误表达式可以看我第一次作业的博客。
6.显示历史记录
数据库可视化软件选择Navicat,后面有同学和我说了更好用的数据库可视化软件,如果下次有机会尝试一下。
7.三角函数
8.log和ln
9.e和π
10. xy和x2
科学计算器和利率计算器的切换
在右下角设置了切换按钮,有一个小小的动画效果,点击即可切换。
利率计算器
1. 活期存款利息计算
2. 定期存款利息计算
3. 活期存款信息修改
4. 定期存款信息修改
5.贷款利息计算
6.贷款信息修改
7.不同功能的切换
下述视频的功能页面顺序为存款利息计算,存款信息修改,贷款利息计算,贷款信息修改。顶部三个按钮中灰色按钮代表该界面实现的是相对应的功能。
设计实现过程
设计原型图
原型图在第一次作业的UI界面的基础上进行了一些优化和改进,仍然采用粉色为主题色。添加了跳转按钮,分别放置在两个页面的左下角和右下角,营造一种翻页的感觉。在设计利率计算器时,一开始可能因为时间紧迫,急于实现功能,而没有充分考虑美观性。最后发现这种选择是明智的,作业差点来不及完成。
![](https://img-blog.csdnimg.cn/02f684d52d074002bef64f9e82ec35f9.png)
![](https://img-blog.csdnimg.cn/d2f1f802935f429a8675387fb4038686.png)
![](https://img-blog.csdnimg.cn/1fd8ca0f003e47edb260c92dbc229743.png)
![](https://img-blog.csdnimg.cn/19383fb0b6184946a01d1d99e91a7ef5.png)
![](https://img-blog.csdnimg.cn/4b9b5c2dc44f4f3db6376c47b3f7674d.png)
用HTML+CSS+JavaScript实现前端
相对于上次使用的Qt,这次选择的工具好用了一万倍!唯一的不足之处在于需要花更多时间去学习,而Qt可以直接在可视化界面上进行设计,然后导出代码,非常适合零基础的用户。然而在实际使用过程中,Qt的操作却相当繁琐。鉴于这次项目要求前后端交互,我决定使用HTML、CSS和JavaScript作为实现工具,根据原型图先实现静态界面,这样做的好处是能够更灵活地控制界面的设计和交互效果。在实现计算器功能之后,对前端进行了进一步的美化,将一些按钮添加动画效果,增加交互性。
1. 鼠标放在科学计算器的按钮上,按钮会变成灰色
2.计算机转换按钮添加小动画
3.利率计算器“活期存款”“定期存款”“清空”等按钮鼠标在上面悬空按钮会浮动
用python的flask框架实现前后端交互
因为在第一次作业中使用Python,并且知道自己还没有能力从头开始使用全新的语言和框架,所以这次我仍然选择Python作为开发语言,并且决定使用Flask框架。Flask是一个轻量级的微框架,它提供了基本的功能,但不会强加过多的复杂性。这使得它非常容易学习和上手,特别适合新手开发者。因此,我决定使用Flask框架并借助fetch实现前后端之间的交互。
代码说明
全部代码有点冗长,如果感兴趣的话可以通过我的gitcode链接查看完整代码。
fetch实现前后端的链接
fetch是JavaScript提供的用于在浏览器中发送网络请求的API。它可以向后端服务器发送HTTP请求并接收响应,从而实现前后端的通信。如:
function calculateResult() {
let expression = document.getElementById("display").value;
fetch('/calculator', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `expression=${encodeURIComponent(expression)}`
})
.then(response => response.text())
.then(result => {
if(['0','1','2','3','4','5','6','7','8','9','-'].includes(result[0])){
document.getElementById("display").value = result;
}else{
setTimeout(function() {
alert(result);
}, 2);
}
});
}
Flask框架
Flask 提供了一个简单而灵活的方式来构建Web应用程序,同时也允许开发者根据项目的需求选择性地扩展其功能。它的轻量级特性使得可以很容易地上手,并且非常适合快速开发小型到中型规模的Web应用。
from flask import Flask
from flask import render_template
from flask import request
from flask import jsonify
app = Flask(__name__)
@app.route('/feiling/calculator')
def template(): # put application's code here
return render_template('index.html')
···
科学计算器
实现界面
首先实现基本界面,并为每个按钮添加相应的运算功能。在输入的同时进行一些简单的错误输入判断,如对"++"、"sin(÷"等错误输入进行检测,通过弹出警告弹窗在网页顶部提醒用户。用户需要点击确认按钮才能关闭弹窗,起到提醒作用。
处理字符串
将字符串传递到后端进行处理,可以进行复杂错误的检测。后端处理还支持嵌套运算,可以确保正确的运算结果,并将其传递回前端显示。
function setString(c) {
if(['+','×','÷'].includes(c)){
if (display.value.length > 0 && (isNumeric(display.value[display.value.length - 1]) || display.value[display.value.length - 1] == ')' || display.value[display.value.length - 1] == 'e' || display.value[display.value.length - 1] == 'π')) {
display.value += c;
} else{
display.value += c;
setTimeout(function() {
alert("表达式输入错误,"+c+"的使用不正确,请仔细检查后正确输入");
}, 2); // 1000毫秒 = 1秒
}
}else if(c == '-'){
if (display.value.length > 0 && (isNumeric(display.value[display.value.length - 1]) || display.value[display.value.length - 1] == ')' || display.value[display.value.length - 1] == 'e' || display.value[display.value.length - 1] == 'π'|| display.value[display.value.length - 1] == '(')) {
display.value += "-";
}else{
display.value += "-";
setTimeout(function() {
alert("表达式输入错误,-的使用不正确,请仔细检查后正确输入");
}, 2);
}
}else if(c=='('){
if (display.value == "0" || display.value == "0.0") {
display.value = "(";
} else if (['+', '-', '*', '÷', '('].includes(display.value.charAt(display.value.length - 1))) {
display.value += '(';
}else{
display.value += '(';
setTimeout(function() {
alert("表达式输入错误,(的使用不正确,请仔细检查后正确输入");
}, 2);
}
}else if(c==')'){
if (display.value.length > 0 && (/\d/.test(display.value.slice(-1)) || display.value.slice(-1) == 'e' || display.value.slice(-1) == 'π' || display.value.slice(-1) == ')') && display.value.includes('(') && display.value.split('(').length > display.value.split(')').length) {
display.value += ')';
}
else{
display.value += ')';
setTimeout(function() {
alert("表达式输入错误,)的使用不正确,请仔细检查后正确输入");
}, 2);
}
}else if(["sin", "sinh", "cos", "cosh","tan","tanh"].includes(c)){
if (display.value == "0" || display.value == "0.0") {
display.value = c+"(";
} else if (['+', '-', '*', '÷', '('].includes(display.value.slice(-1))) {
display.value += c+"(";
}else{
display.value += c+"(";
setTimeout(function() {
alert("有关"+c+"的操作不符合要求,请重新输入");
}, 2);
}
}else if(["π","e"].includes(c)){
if (display.value == "0" || display.value == "0.0") {
display.value = c;
} else if (!(/[0-9\)]$/.test(display.value))) {
display.value += c;
}else{
display.value += c;
setTimeout(function() {
alert("有关"+c+"的操作不符合要求,请重新输入");
}, 2);
}
}else if(c== '^'){
if (["e","π",")"].includes(display.value[display.value.length - 1])||isNumeric(display.value[display.value.length - 1])) {
display.value += "^(";
}else{
display.value += "^(";
setTimeout(function() {
alert("有关x^y的操作不符合要求,请重新输入");
}, 2);
}
}else if(c=="^2"){
if (["e","π",")"].includes(display.value[display.value.length - 1])||isNumeric(display.value[display.value.length - 1])) {
display.value += "^2";
}else{
display.value += "^2";
setTimeout(function() {
alert("有关x^2的操作不符合要求,请重新输入");
}, 2);
}
}else if(["ln","log"].includes(c)){
if (display.value == "0" || display.value == "0.0") {
display.value = c+"(";
} else if (['+', '-', '*', '÷', '('].includes(display.value.charAt(display.value.length - 1))) {
display.value += c+"(";
}else{
display.value += c+"(";
setTimeout(function() {
alert("有关"+c+"的操作不符合要求,请重新输入");
}, 2);
}
}else if(c=='%'){
if (display.value.length > 0 && (isNumeric(display.value[display.value.length - 1]) || display.value[display.value.length - 1] == ')' || display.value[display.value.length - 1] == 'e' || display.value[display.value.length - 1] == 'π')) {
display.value += c;
} else{
display.value += c;
setTimeout(function() {
alert("表达式输入错误,"+c+"的使用不正确,请仔细检查后正确输入");
}, 2); // 1000毫秒 = 1秒
}
}
}
其他更详细的有关处理字符串的公式可以看我第一次作业的博客。
利率计算器
连接数据库
作业要求有关利率表的数值从数据库调用,选用Mysql进行操作。
conn = pymysql.connect(
host='localhost',
port=3306,
user='root',
password='******',
database='calculator'
)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS calculations (
id INT AUTO_INCREMENT PRIMARY KEY,
expression VARCHAR(255),
result FLOAT
)
""")
界面切换
利率计算器相对复杂的地方在于需要根据不同的功能展示不同的界面,与科学计算器不同,它的界面需求更加灵活。
在实现利率计算器时,我学到了许多有关界面设计的技巧。通过将不需要显示的界面属性设置为隐藏 style="display: none;,在需要时将属性改成**“block”**灵活切换界面,使用户可以在不同功能间进行切换。例如,下图展示了存款利率的修改和存款利率计算两个功能,当用户切换到某个功能时,对应的按钮会变为灰色,以便用户清晰地知道当前所处的功能模块。
<div id="change loan rate" style="display: none;">
function switchToChangeLoanRate() {
document.getElementById("calculator").style.display = "none";
document.getElementById("rate calculator save").style.display = "none";
document.getElementById("rate calculator loan").style.display = "none";
document.getElementById("change save rate").style.display = "none";
document.getElementById("change loan rate").style.display = "block";
}
这种设计使得利率计算器具有更强的适应性和用户友好性,让用户能够更方便地进行操作。
关于存款功能分析
-
计算存款利息
输入存款金额和存款时长信息。
存款利息 = 存款金额 × 存款利率 × 存款时长 存款利息=存款金额\times存款利率\times存款时长 存款利息=存款金额×存款利率×存款时长
-
活期存款
从与后端连接的数据库中获取活期存款的利率,这个利率不会因存款时间的长短而改变。然后将计算结果发送到前端以供展示。
@app.route('/cal_time', methods=['POST'])
def cal_time():
time = request.form['time']
money = request.form['money']
insert = "SELECT rate FROM current_account_deposit"
cursor.execute(insert)
data = cursor.fetchall()
if not data:
return "error"
-
定期存款
根据用户输入的存款时长,从与后端链接的数据库中获取相应的利率,并将利息计算结果发送至前端进行展示。
@app.route('/cal_demand', methods=['POST'])
def cal_demand():
time = request.form['time']
money = request.form['money']
insert = "SELECT rate FROM Fixed_Deposit_Interest_Rate WHERE time = " + time
cursor.execute(insert)
data = cursor.fetchall()
if not data:
return "error"
else:
data=data[0][0]
result = data * float(money) * float(time) / 100.0
return str(result)
- 修改存款利息信息
-
活期利率修改
对活期利率进行修改时,可以选择不填写存款时长,直接更新存储在数据库中的活期利率。
@app.route('/change_save_time', methods=['POST'])
def change_save_time():
tax_rate = request.form['tax_rate']
insert = "UPDATE current_account_deposit SET rate = " + tax_rate
cursor.execute(insert)
conn.commit()
return "finish"
-
定期利率修改
当进行定期利率的修改时,需要输入新的存款利率和存款时长,以便更新存储在数据库中的定期利率信息。
点击“修改活期”或“修改定期”按钮后,系统将进行相应的数据库修改操作。操作完成后,将弹出一个名为“finish”的提示窗口,以通知用户修改已成功完成。
@app.route('/change_save_demand', methods=['POST'])
def change_save_demand():
tax_rate = request.form['tax_rate']
tax_time = request.form['tax_time']
insert = "UPDATE Fixed_Deposit_Interest_Rate SET rate = " + tax_rate + " WHERE time = " + tax_time
cursor.execute(insert)
conn.commit()
return "finish"
关于贷款功能分析
- 计算贷款利息
贷款利息 = 贷款金额 × 贷款利率 × 贷款时长 贷款利息=贷款金额\times贷款利率\times贷款时长 贷款利息=贷款金额×贷款利率×贷款时长
在输入贷款金额和贷款时长后,从可选的贷款利率时长中进行选择,如“六个月”、“一年”等。系统会根据选择从与后端链接的数据库中获取相应的利率,并计算出相应的利息并将利息计算结果发送至前端展示。
@app.route('/cal_loan', methods=['POST'])
def cal_loan():
time = request.form['loan_time']
time2 = request.form['loantime']
money = request.form['loan_money']
insert = "SELECT rate FROM demand_deposit WHERE time = '" + time +"'"
cursor.execute(insert)
data = cursor.fetchall()
if not data:
return "error"
else:
data=data[0][0]
result = data * float(money) * float(time2) / 100.0
return str(result)
- 修改贷款信息
从可选的贷款利率时长中进行选择,如“六个月”、“一年”等。在输入贷款利率后,点击“确认修改”按钮,系统将对相应的数据库进行修改操作。操作完成后,弹出一个名为“finish”的提示窗口,通知用户修改已成功完成。
@app.route('/change_loan', methods=['POST'])
def change_loan():
change_loan_tax = request.form['change_loan_tax']
change_loan_time = request.form['change_loan_time']
insert = "UPDATE demand_deposit SET rate = " + change_loan_tax + " WHERE time = '" + change_loan_time+"'"
cursor.execute(insert)
conn.commit()
return "finish"
两个计算器的切换
通过将不需要显示的计算器界面属性设置为隐藏,在需要时灵活切换界面,使用户可以在不同计算器间进行切换。
功能结构图
心路历程和收获
通过这个项目,我学到了如何处理前后端交互,如何利用Flask框架搭建后端服务。也学到了如何设计并管理一个简单的数据库用于存储利率信息,在操作过程中加深了对HTML、CSS和JavaScript的理解,并学会了如何将它们结合起来构建一个完整的前端应用。通过解决各种错误和挑战,提高了问题解决和调试的能力。最重要的是,我完成了一个功能完整的项目!这使我对前后端交互有了更深入的理解和信心。最大的心愿是下次写作业不要这么仓促,但这也证明我的能力还有欠缺,还需要多多学习!多多努力!