# 通貨膨脹-台鐵便當 ### 選擇原因: * 便捷性: 適合上班族、學生和經常外出的人士,隨時隨地享用家常美食。 保溫功能強大,適合各種場合使用,如戶外活動、辦公室午餐等。 * 文化與情感價值: 台鐵便當盒在許多文化中象徵著家人的關愛和溫暖,特別是家長為孩子準備的便當中,承載著情感價值。 ### SQLlit ![螢幕擷取畫面 2024-05-22 011600](https://hackmd.io/_uploads/H1PTVucmC.png) ## Code+解釋 ### 前端 1. 頭部 (Head): * 設置字符編碼為UTF-8。 * 設置視口以支持響應式設計。 * 設置頁面標題。 * 引入CSS樣式和Chart.js庫。 2. 表單 (Form): * 包含日期選擇器(開始日期和結束日期)和便當種類選擇下拉菜單。 * 提交按鈕。 3. 畫布 (Canvas): * 用於顯示折線圖的畫布元素。 :::spoiler **HTML** ````javascript= <!DOCTYPE html> <html lang="zh-Hant"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>台鐵便當價格趨勢</title> <link rel="stylesheet" href="styles.css"> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> </head> <body> <form id="data-form"> <label for="startDate">開始日期:</label> <input type="date" id="startDate" name="startDate"> <label for="endDate">結束日期:</label> <input type="date" id="endDate" name="endDate"> <label for="bento">便當種類:</label> <select id="bento" name="bento"> <option value="臺鐵排骨便當(經濟)">臺鐵排骨便當(經濟)</option> <option value="古早味爌肉便當">古早味爌肉便當</option> <option value="照燒雞丁便當">照燒雞丁便當</option> <option value="臺鐵素食便當(白飯)">臺鐵素食便當(白飯)</option> <option value="臺鐵素食便當(燉飯)">臺鐵素食便當(燉飯)</option> <option value="臺鐵排骨便當(八角)">臺鐵排骨便當(八角)</option> <option value="五行素便當">五行素便當</option> <option value="迷迭香雞腿排便當">迷迭香雞腿排便當</option> <option value="臺鐵雞腿便當(圓紙)">臺鐵雞腿便當(圓紙)</option> <option value="臺鐵鯖魚便當(圓紙)">臺鐵鯖魚便當(圓紙)</option> <option value="懷舊排骨菜飯(圓木)">懷舊排骨菜飯(圓木)</option> <option value="客家紅麴雞便當">客家紅麴雞便當</option> <option value="薑汁燒肉飯">薑汁燒肉飯</option> <option value="芋香牛蒡飯">芋香牛蒡飯</option> </select> <button type="submit">提交</button> </form> <canvas id="myChart"></canvas> <script> document.getElementById('data-form').addEventListener('submit', function(event) { event.preventDefault(); const startDate = document.getElementById('startDate').value; const endDate = document.getElementById('endDate').value; const bento = document.getElementById('bento').value; fetch(`/api/prices?startDate=${startDate}&endDate=${endDate}&bento=${bento}`) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { const dates = data.prices.map(price => new Date(price.date)); const prices = data.prices.map(price => price.price); drawChart(dates, prices); }) .catch(error => { console.error('Error:', error); // Handle the error }); }); function drawChart(dates, prices) { const ctx = document.getElementById('myChart').getContext('2d'); if (window.myChart) { window.myChart.destroy(); } window.myChart = new Chart(ctx, { type: 'line', data: { labels: dates, datasets: [{ label: 'Price Trend', data: prices, borderColor: 'rgb(75, 192, 192)', tension: 0.1 }] }, options: { scales: { x: { type: 'time', time: { unit: 'month', displayFormats: { month: 'YYYY-MM' } }, title: { display: true, text: 'Time' } }, y: { title: { display: true, text: 'Price' } } } } }); } </script> </body> </html> ```` ::: ### 後端 1. 引入套件: * express: 這是一個流行的 Node.js Web 框架,用於處理 HTTP 請求和路由。 * path: Node.js 的內置模組,用於處理文件路徑。 * sqlite3: 這是 SQLite 資料庫的 Node.js 綁定,用於操作 SQLite 資料庫。 * cors: 這是一個 Express 中間件,用於處理跨來源資源共享(CORS)。 2. 建立 Express 應用程式實例: * 使用 express() 建立了一個 Express 應用程式。 3. 設置中間件: * cors():啟用 CORS 支持,允許跨來源請求。 4. 連接資料庫: * 使用 sqlite3 打開 SQLite 資料庫連接。 * 如果連接成功,則創建一個名為 bento_prices 的資料表,該表包含 id、date、bento_type 和 price 欄位。 5. 定義路由: * GET /api/prices:當收到此路由的 GET 請求時,使用提供的查詢參數(startDate、endDate 和 bento)從資料庫中檢索對應的價格資料。 * 如果查詢成功,將結果以 JSON 格式返回,並將日期格式化為 ISO 8601 標準。 6. 設置靜態資源路徑: * 使用 express.static 中間件指定了靜態資源的路徑,這將使 Express 伺服器能夠提供靜態檔案(如 HTML、CSS、JavaScript)。 7. 啟動伺服器: * 使用 app.listen() 啟動了一個 HTTP 伺服器,監聽在本地端口 3000 上。 :::spoiler **app.js** ````javascript= const express = require('express'); const path = require('path'); const sqlite3 = require('sqlite3').verbose(); const cors = require('cors'); const app = express(); app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: false })); const dbPath = path.join(__dirname, 'db', 'sqlite.db'); const db = new sqlite3.Database(dbPath, (err) => { if (err) { console.error('Error connecting to database:', err.message); } else { console.log('Connected to SQLite database:', dbPath); db.run(`CREATE TABLE IF NOT EXISTS bento_prices ( id INTEGER PRIMARY KEY AUTOINCREMENT, date DATE NOT NULL, bento_type TEXT NOT NULL, price REAL NOT NULL )`, (err) => { if (err) { console.error('Error creating bento_prices table:', err.message); } else { console.log('bento_prices table created successfully.'); } }); } }); // 查询价格趋势 app.get('/api/prices', (req, res) => { const { startDate, endDate, bento } = req.query; const sql = ` SELECT date, price FROM bento_prices WHERE bento_type = ? AND date BETWEEN ? AND ? ORDER BY date `; db.all(sql, [bento, startDate, endDate], (err, rows) => { if (err) { console.error('Error executing query:', err.message); res.status(500).json({ error: err.message }); return; } console.log('Query results:', rows); // 格式化日期為 ISO 8601 格式 const formattedRows = rows.map(row => ({ date: new Date(row.date).toISOString(), price: parseFloat(row.price) })); res.json({ prices: formattedRows }); }); }); app.use(express.static(path.join(__dirname, 'public'))); const PORT = 3000; app.listen(PORT, () => { console.log(`Server running at http://localhost:${PORT}`); }); module.exports = app; ```` :::