Crafting a Web Calculator with MySQL and Node.js

832101123高铭浩 2023-10-20 12:19:20
Course for This Assignmenthttps://bbs.csdn.net/forums/ssynkqtd-04
Assignment Requirementshttps://bbs.csdn.net/topics/617378696
Objectives of This AssignmentComplete a visual calculator with front end separation
Other References------
MU STU ID and FZU STU ID21125341_832101123

GitHub_Frontend_code
GitHub_Backend_code

Content

  • 1. PSP
  • 2. Design & Implementation
  • 2.1. Overview
  • 2.2. Functional Structure Diagram
  • 3. Function Demonstration
  • 4. Code description
  • 4.1. Front-end
  • 4.1.1 JavaScript
  • 4.1.2 HTML
  • 4.1.3 CSS
  • 4.2. Back-end
  • 4.2.1 JavaScript
  • Summary

1. PSP

Personal Software Process StagesEstimated Time (minutes)Actual Time (minutes)
Planning3036
• Estimate1518
Development300300
• Analysis3036
• Design Spec4048
• Coding Standard1518
• Design6072
• Coding120300
• Code Review2024
• Test1518
Reporting3036
• Test Report1518
• Postmortem & Process Improvement Plan1518
Sum330426

2. Design & Implementation

2.1. Overview

This assignment involves implementing a separated frontend and backend calculator. In the previous assignment, I utilized the WeChat Mini Program Developer Tools for a visual calculator. Due to my limited knowledge of backend aspects in WeChat Mini Program, I opted for js+html+css for the frontend and MySQL database, Node.js environment, and the express framework for the backend. The frontend features a calculator with a content box and buttons, including a screen displaying historical data. To facilitate network communication, I employed fetch to transmit frontend data to the backend.

2.2. Functional Structure Diagram

img

3. Function Demonstration

img

4. Code description

4.1. Front-end

4.1.1 JavaScript

This part of the code defines a set of functions for a calculator application. These functions collectively provide the basic calculator functionalities, including arithmetic operations, exponentiation, factorial, and trigonometric functions. The calculate() function uses eval() to evaluate expressions, and there's error handling for invalid calculations. Additionally, the code replaces π with Math.PI for accurate evaluation.

function appendToDisplay(value) {
  display.value += value;
  currentExpression += value;
}

// Function to handle multiplication operation
function multiply() {
  display.value += '×';
  currentExpression += '*';
}

// Function to handle division operation
function divide() {
  display.value += '÷';
  currentExpression += '/';
}

// Function to handle power/exponentiation operation
function power() {
  display.value += '^';
  currentExpression += '**';
}

// Function to handle complementation operation
function complementation() {
  display.value += '%';
  currentExpression += '%';
}

// Function to clear the display and current expression
function clearDisplay() {
  display.value = '';
  currentExpression = '';
}

// Function to delete the last element from the display and current expression
function deleteElement() {
  display.value = display.value.slice(0, -1);
  currentExpression = currentExpression.slice(0, -1);
}

// Function to perform calculation
function calculate() {
  try {
    // Replace π with Math.PI to prevent eval issues with π
    var expression = currentExpression.replace(/π/g, 'Math.PI');
    // Evaluate the expression and update the display
    display.value = eval(expression);
    // Check if there's a valid expression and it's not 'undefined'
    if (currentExpression && currentExpression !== 'undefined') {
      // Add the calculation history record
      addHistory({ note: `${currentExpression} = ${display.value.toString()}` });
    }
    // Update the current expression with the calculated result
    currentExpression = display.value.toString();
  } catch (error) {
    // Handle calculation errors by displaying 'Error'
    display.value = 'Error';
  }
}

// Function to calculate factorial
function factorial() {
  var num = parseFloat(display.value);

  if (isNaN(num) || num < 0) {
    // Display 'Error' for invalid input or negative numbers
    display.value = 'Error';
    return;
  }

  var result = 1;

  // Calculate factorial
  for (var i = 2; i <= num; i++) {
    result *= i;
  }

  // Update display and current expression with the result
  display.value = result;
  currentExpression = result.toString();
}

// Function to add square to the expression
function square() {
  display.value += '²';
  currentExpression += '**2';
}

// Function to handle square root
function squareRoot() {
  if (currentExpression.endsWith('Math.sqrt(')) {
    // If already dealing with square root, complete the expression
    display.value = display.value.slice(0, -1) + ')';
    currentExpression = currentExpression.slice(0, -9) + ')';
  } else {
    // If not, add the square root function to the expression
    display.value += '√(';
    currentExpression += 'Math.sqrt(';
  }
}

// Functions to handle trigonometric functions
function sine() {
  display.value += 'sin(';
  currentExpression += 'Math.sin(';
}

function cosine() {
  display.value += 'cos(';
  currentExpression += 'Math.cos(';
}

function tangent() {
  display.value += 'tan(';
  currentExpression += 'Math.tan(';
}

Next part of the code handles communication with a server through API calls for managing calculator history.

  • baseUrl: Defines the base URL for API calls, pointing to 'http://localhost:3000'/.
  • getData(limit, type): Fetches data from the server using the /api/get_history endpoint with specified limits. It handles the response, updates the noteList variable, and modifies the display based on the provided type ('input' or default). If 'input' is specified, it updates the calculator display with the last calculation result from the history.
  • addHistory(data): Sends a POST request to the server's /api/add_history endpoint to add a new history record. It expects a JSON response and triggers a data refresh using getData(10).
  • renderList(): Renders the history list by creating HTML elements for each history record and updating the content of the .history_container element.

Event listener for #ans element: Listens for a click on the 'ans' element and triggers a call to getData(1, 'input'), updating the display with the last calculation result.
The code relies on the Fetch API to perform asynchronous requests to the server. It also includes error handling for both fetching and adding history records. Additionally, there's an event listener for the ans element to retrieve and display the last calculation result when clicked.

// Base URL for API calls
const baseUrl = 'http://localhost:3000';

// Function to fetch data from the server
function getData(limit, type) {
  fetch(baseUrl + `/api/get_history?limit=${limit}`)
    .then(response => {
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      return response.json();
    })
    .then(data => {
      console.log(data);
      noteList = data;
      if (type === 'input') {
        display.value = noteList.length ? noteList[noteList.length - 1]['note'].split('=')[1].trim() : '';
        currentExpression = display.value;
      } else {
        renderList();
      }
    })
    .catch(error => {
      console.log('Fetch error: ', error);
      noteList = [];
      renderList();
    });
}

// Function to add history record
function addHistory(data) {
  fetch(baseUrl + '/api/add_history', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  })
    .then(response => response.json())
    .then(data => {
      console.log(data);
      if (data && data.id) {
        getData(10);
      }
    })
    .catch(error => {
      console.error('Error:', error);
    });
}

// Function to render history list
function renderList() {
  let html = '';
  noteList.forEach((i, j) => {
    html += `<p>${j + 1}. ${i.note}</p>`;
  });
  document.querySelector('.history_container').innerHTML = html;
}

// Event listener for 'ans' element click event
document.querySelector('#ans').addEventListener('click', function () {
  getData(1, 'input');
});

4.1.2 HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- Document metadata -->
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Dark Simple Calculator</title>
    <!-- External stylesheet link -->
    <link rel="stylesheet" href="styles.css" />
  </head>

  <body>
    <!-- Calculator container -->
    <div class="container">
      <div id="calculator">
        <!-- Display input container -->
        <div class="input-container">
          <input type="text" id="display" disabled />
        </div>
        <!-- Button rows -->
        <div class="button-row">
          <!-- Trigonometric functions and constants -->
          <input type="button" value="sin" onclick="sine()" />
          <input type="button" value="cos" onclick="cosine()" />
          <input type="button" value="tan" onclick="tangent()" />
          <input type="button" value="π" onclick="appendToDisplay('π')" />
          <input type="button" value="C" onclick="clearDisplay()" />
        </div>
        <div class="button-row">
          <!-- Basic arithmetic operations -->
          <input type="button" value="+" onclick="appendToDisplay('+')" />
          <input type="button" value="-" onclick="appendToDisplay('-')" />
          <input type="button" value="×" onclick="multiply()" />
          <input type="button" value="÷" onclick="divide()" />
          <input type="button" value="^" onclick="power()" />
        </div>
        <div class="button-row">
          <!-- Percentage, numbers, and backspace -->
          <input type="button" value="%" onclick="complementation()" />
          <input type="button" value="7" class="hover" onclick="appendToDisplay('7')" />
          <input type="button" value="8" class="hover" onclick="appendToDisplay('8')" />
          <input type="button" value="9" class="hover" onclick="appendToDisplay('9')" />
          <input type="button" value="⌫" onclick="deleteElement()" />
        </div>
        <div class="button-row">
          <!-- Squaring, more numbers, and parentheses -->
          <input type="button" value="x²" onclick="square()" />
          <input type="button" value="4" class="hover" onclick="appendToDisplay('4')" />
          <input type="button" value="5" class="hover" onclick="appendToDisplay('5')" />
          <input type="button" value="6" class="hover" onclick="appendToDisplay('6')" />
          <input type="button" value="(" onclick="appendToDisplay('(')" />
        </div>
        <div class="button-row">
          <!-- Factorial, more numbers, and parentheses -->
          <input type="button" value="x!" onclick="factorial()" />
          <input type="button" value="1" class="hover" onclick="appendToDisplay('1')" />
          <input type="button" value="2" class="hover" onclick="appendToDisplay('2')" />
          <input type="button" value="3" class="hover" onclick="appendToDisplay('3')" />
          <input type="button" value=")" onclick="appendToDisplay(')')" />
        </div>
        <div class="button-row">
          <!-- Square root, decimal point, zero, equals, and answer -->
          <input type="button" value="√" onclick="squareRoot()" />
          <input type="button" value="." onclick="appendToDisplay('.')" />
          <input type="button" value="0" onclick="appendToDisplay('0')" />
          <input type="button" value="=" onclick="calculate()" />
          <input type="button" value="ans" id="ans" />
        </div>
      </div>
      <!-- History container -->
      <div class="history_container"></div>
    </div>

    <!-- JavaScript file inclusion -->
    <script src="calculator.js"></script>
  </body>
</html>

4.1.3 CSS

/* Body styling */
body {
    background-color: #383838;
    height: 100vh;
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
}

/* Main container styling */
.container {
    display: flex;
    justify-content: center;
    /* align-items: center; */
}

/* Calculator styling */
#calculator {
    background-color: #333333;
    border: 2px solid #ffffff75;
    border-radius: 20px;
    padding: 40px;
    width: 500px;
    margin-right: 20px;
    /* Adjust spacing as needed */
    height: fit-content;
}

/* Input container styling */
.input-container {
    display: flex;
    justify-content: center;
    align-items: center;
}

/* Text input styling */
input[type="text"] {
    width: 100%;
    padding: 40px;
    font-size: 30px;
    margin-bottom: 20px;
    text-align: right;
}

/* Button row styling */
.button-row {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    grid-gap: 10px;
}

/* Button styling */
input[type="button"] {
    padding: 10px;
    font-size: 20px;
    background-color: #333333;
    /* Modify color */
    border: none;
    color: #cfcfc4;
    /* Modify color */
    cursor: pointer;
}

/* Button hover effect */
input[type="button"]:hover {
    background-color: #8a8686a8;
    /* Modify color */
}

/* Display screen styling */
#display-screen {
    background-color: #000000;
    border: 2px solid #ffffff75;
    border-radius: 10px;
    padding: 10px;
    width: 200px;
    /* Adjust width as needed */
    text-align: right;
    font-size: 24px;
    color: #ffffff;
}

/* History container styling */
.history_container {
    background-color: #333333;
    border: 2px solid #ffffff75;
    border-radius: 20px;
    padding: 15px 40px 0 40px;
    width: 400px;
    margin-right: 20px;
    display: flex;
    flex-direction: column;
    color: #fff;
    min-height: 450px;
}

/* History paragraph styling */
.history_container p{
    line-height: 24px;
    margin: 10px 0;
}

4.2. Back-end

4.2.1 JavaScript

This is a Node.js server using Express, serving as the backend for the calculator application.

  1. Dependencies: The server uses several npm packages, including express for the web server, body-parser for handling POST data, cors for Cross-Origin Resource Sharing, and mysql for interacting with a MySQL database.

    const express = require('express');
    const bodyParser = require('body-parser');
    const cors = require('cors');
    const mysql = require('mysql');
    
  2. Express Setup: Initializes the Express application and sets the port to 3000.

    const app = express();
    const PORT = 3000;
    app.use(cors());
    
  3. MySQL Connection: Establishes a connection to a MySQL database using the mysql package. Connection details (hostname, username, password, and database name) are specified.

    // MySQL connection settings
    const db = mysql.createConnection({
    host: 'localhost',   // Replace with your MySQL server hostname
    user: 'root',        // Replace with your MySQL database username
    password: 'root',    // Replace with your MySQL database password
    database: 'cal'      // Replace with your MySQL database name
    });
    // Connect to the MySQL database
    db.connect((err) => {
    if (err) throw err;
    console.log('Connected to the database.');
    });
    
  4. Body-Parser Middleware: Configures Express to use body-parser middleware for handling POST data.

    // Use body-parser middleware to handle POST data
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(bodyParser.json());
    
  5. API Endpoints:
    /api/get_history: Responds to GET requests, retrieves history records from the t_history table in the database, and sends them as a JSON response. It accepts a limit query parameter to specify the number of records to retrieve.
    /api/add_history: Responds to POST requests, expects a JSON payload with a note property, inserts a new record into the t_history table, and responds with a success message and the ID of the inserted record.

    // API endpoint to get history records
    app.get('/api/get_history', (req, res) => {
    // Get the 'limit' value from the query parameters of the request. Default to 10 if not provided.
    let limit = req.query.limit ? parseInt(req.query.limit) : 10;
    // Prevent SQL injection: Ensure the limit value is a number
    if (isNaN(limit)) {
     res.status(400).json({ error: 'Invalid limit value' });
     return;
    }
    // Query the database with the specified limit
    db.query('SELECT * FROM t_history ORDER BY update_time DESC LIMIT ?', [limit], (err, rows) => {
     if (err) {
       res.status(500).json({ error: err.message });
       return;
     }
     res.json(rows);
    });
    });
    // API endpoint to add a new history record
    app.post('/api/add_history', (req, res) => {
    const { note } = req.body;
    if (!note) {
     res.status(400).json({ error: 'Please provide a history record' });
     return;
    }
    const history = { note };
    const query = 'INSERT INTO t_history SET ?';
    // Insert the new history record into the database
    db.query(query, history, (err, result) => {
     if (err) {
       res.status(500).json({ error: err.message });
       return;
     }
     // Respond with a success message and the ID of the inserted record
     res.json({ message: 'Record added successfully!', id: result.insertId });
    });
    });
    
  6. Server Start: Finally, the server is started, listening on port 3000.

    // Start the server
    app.listen(PORT, () => {
    console.log(`Server is running at http://localhost:${PORT}`);
    });
    

Summary

In tackling this assignment, the challenge lay in creating a frontend and backend calculator. While I had prior experience designing a visual calculator using the WeChat Mini Program Developer Tools, my knowledge of the backend aspects in the WeChat Mini Program was limited. Consequently, I chose to implement the frontend using js+html+css. For the backend, I opted for a MySQL database, Node.js environment, and the Express framework. The frontend component comprises a calculator with a content box and buttons, along with a screen displaying historical data. To achieve seamless communication between the frontend and backend, I delved into and applied the fetch function to transmit frontend data to the backend.

Through this process, I gained a deeper understanding of the frontend-backend separation development approach, clarifying the collaborative nature of frontend and backend work. Additionally, my proficiency in utilizing MySQL databases and the Node.js environment was strengthened. Overall, this assignment provided valuable insights into building comprehensive applications, positively impacting my skill set for future development projects.

...全文
89 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
Learn how to turn raw data into rich, interactive web visualizations with the powerful combination of Python and JavaScript. With this hands-on guide, author Kyran Dale teaches you how build a basic dataviz toolchain with best-of-breed Python and JavaScript libraries—including Scrapy, Matplotlib, Pandas, Flask, and D3—for crafting engaging, browser-based visualizations. As a working example, throughout the book Dale walks you through transforming Wikipedia’s table-based list of Nobel Prize winners into an interactive visualization. You’ll examine steps along the entire toolchain, from scraping, cleaning, exploring, and delivering data to building the visualization with JavaScript’s D3 library. If you’re ready to create your own web-based data visualizations—and know either Python or JavaScript— this is the book for you. Learn how to manipulate data with Python Understand the commonalities between Python and JavaScript Extract information from websites by using Python’s web-scraping tools, BeautifulSoup and Scrapy Clean and explore data with Python’s Pandas, Matplotlib, and Numpy libraries Serve data and create RESTful web APIs with Python’s Flask framework Create engaging, interactive web visualizations with JavaScript’s D3 library Table of Contents Chapter 1 Development Setup Chapter 2 A Language-Learning Bridge Between Python and JavaScript Chapter 3 Reading and Writing Data with Python Chapter 4 Webdev 101 Chapter 5 Getting Data off the Web with Python Chapter 6 Development Setup Chapter 7 A Language-Learning Bridge Between Python and JavaScript Chapter 8 Reading and Writing Data with Python Chapter 9 Webdev 101 Chapter 10 Getting Data off the Web with Python Chapter 11 Heavyweight Scraping with Scrapy Chapter 12 Introduction to NumPy Chapter 13 Introduction to Pandas Chapter 14 Cleaning Data with Pandas Chapter 15 Visualizing Data with Matplotlib Chapter 16 Exploring Data with Pandas Chapter 17 Delivering the Data Chapter 18 RESTful Data with Flask Chapter 19 Imagining a Nobel Visualization Chapter 20 Building a Visualization Chapter 21 Introducing D3—The Story of a Bar Chart Chapter 22 Visualizing Individual Prizes Chapter 23 Mapping with D3 Chapter 24 Visualizing Individual Winners Chapter 25 The Menu Bar Chapter 26 Conclusion
Design and deliver an optimal user experience for all devices About This Book Get to grips with the core functionality of RWD through examples Discover how to make layouts, content and media flexible, and explore why a content-first approach is more effective Maximize the performance of your web pages so that they work across all browsers and devices irrespective of the screen size Who This Book Is For This book is for web designers who are familiar with HTML and CSS, and want to begin with responsive web design. Web development experience and knowledge of HTML5, CSS3 is assumed. What You Will Learn Explore various layout options Understand what can be achieved in the browser, without the use of third-party tools Executing media queries to benefit responsive designs Understand the basics of responsive workflow and boilerplate frameworks Improve performance of responsive web design Maintain compatibility across various browsers In Detail Responsive web design (RWD) is a web design approach aimed at crafting sites to provide an optimal viewing and interaction experience - providing easy reading and navigation with minimum resizing, panning, and scrolling - and all of this across a wide range of devices from desktop computer monitors to mobile phones. Responsive web design is becoming more important as the amount of mobile traffic now accounts for more than half of the Internet’s total traffic. This book will give you in depth knowledge about the basics of responsive web design. You will embark on a journey of building effective responsive web pages that work across a range of devices, from mobile phones to smart TVs, with nothing more than standard markup and styling techniques. You'll begin by getting an understanding of what RWD is and its significance to the modern web. Building on the basics, you'll learn about layouts and media queries. Following this, we’ll dive into creating layouts using grid based Table of Contents Chapter 1: Introducing Responsive Web Design Chapter 2: Creating Fluid Layouts Chapter 3: Adding Responsive Media Chapter 4: Exploring Media Queries Chapter 5: Testing and Optimizing for Performance

176

社区成员

发帖
与我相关
我的任务
社区描述
梅努斯软件工程
软件工程 高校 福建省·福州市
社区管理员
  • LinQF39
  • Jcandc
  • chjinhuu
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧