iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
Modern Web

TypeScript 啟動!系列 第 25

[Day 25] TypeScript 零售業者模擬演練 III

  • 分享至 

  • xImage
  •  

顯然跟目標網頁比較還有點小地方可以調整,主要是 CSS 上面的調整。

CSS 是一種描述語言,用於描述 HTML 的呈現方式。也就是說,在網頁上看到各種顏色、佈局和字體設計,背後都是由 CSS 控制和定義的。當然現在通常都是用別人寫好的模組來進行套用,畢竟站在巨人的肩膀上才是更好省力的~

CSS 主要應用:

  1. 佈局和設計:CSS 可用於定義網頁的整體佈局,包括網頁的結構和元素的位置。
  2. 顏色和字體:可以使用 CSS 來設定文字的顏色、大小、間距、行高和字體等。
  3. 響應式設計:隨著移動裝置的普及,響應式網頁設計成為一個重要的概念。使用 CSS,開發者可以使網站在不同大小的裝置上都能正常和優雅地展示。
  4. 動畫和過渡:現代的 CSS 允許開發者添加各種動畫和過渡效果,從簡單的顏色更改到複雜的元素變形都可以。

那接下來首先就是先更改

Step1 表格修改(SalesTable.tsx)

// components/SalesTable.tsx

import React from 'react';
import { SaleRecord } from './types';

type Props = {
    data: SaleRecord[];
};

const SalesTable: React.FC<Props> = ({ data }) => {

    const formatCurrency = (amount: number): string => {
        return new Intl.NumberFormat('zh-TW', { style: 'currency', currency: 'TWD' }).format(amount);
    };

    return (
        <table style={{ width: '100%', borderCollapse: 'collapse' }}>
            <thead>
                <tr style={{ borderBottom: '2px solid black' }}>
                    <th style={{ padding: '8px 15px', textAlign: 'center' }}>週</th>
                    <th style={{ padding: '8px 15px', textAlign: 'center' }}>價錢</th>
                    <th style={{ padding: '8px 15px', textAlign: 'center' }}>存貨</th>
                    <th style={{ padding: '8px 15px', textAlign: 'center' }}>銷售</th>
                    <th style={{ padding: '8px 15px', textAlign: 'center' }}>剩餘存貨</th>
                    <th style={{ padding: '8px 15px', textAlign: 'center' }}>營收</th>
                    <th style={{ padding: '8px 15px', textAlign: 'center' }}>累計營收</th>
                </tr>
            </thead>
            <tbody>
                {data.map(record => (
                    <tr key={record.week}>
                        <td style={{ padding: '8px 15px', textAlign: 'center' }}>{record.week}</td>
                        <td style={{ padding: '8px 15px', textAlign: 'center' }}>{formatCurrency(record.price)}</td>
                        <td style={{ padding: '8px 15px', textAlign: 'center' }}>{record.initialStock}</td>
                        <td style={{ padding: '8px 15px', textAlign: 'center' }}>{record.sales}</td>
                        <td style={{ padding: '8px 15px', textAlign: 'center' }}>{record.remainingStock}</td>
                        <td style={{ padding: '8px 15px', textAlign: 'center' }}>{formatCurrency(record.revenue)}</td>
                        <td style={{ padding: '8px 15px', textAlign: 'center' }}>{formatCurrency(record.accumulatedRevenue)}</td>
                    </tr>
                ))}
            </tbody>
        </table>
    );
};

export default SalesTable;

主要稍微修改一下 CSS 上的呈現,還有價格為台幣千分位之類的~

  • 在 html 上的表個 tr th td 上去做一些描述,透過 style 來進行。
    • width:設定元素的水平寬度。
      • 數值可以:
        • 固定值(如 px, em, rem 等)
        • 相對值(如 %,表示相對於父元素的寬度)
        • auto:由瀏覽器自動計算元素的寬度。
    • borderCollapse:表格邊框合併或分隔。
      • collapse:相鄰的單元格邊框將合併成一個單一邊框。
      • separate:單元格具有各自分開的邊框。
    • padding:元素內部的邊距,即元素邊緣與其內容之間的空間。
    • textAlign :文字與其父元素對齊的方式。
      • 數值:
        • left:內容對齊左側。
        • right:內容對齊右側。
        • center:內容居中對齊。
        • justify:兩側對齊。
  • const formatCurrency : 處理千分位,中間用了 new Intl.NulberFormat 來幫忙處理一下。

Step2. 修改售價(PricingControl.tsx)

// components/PricingControl.tsx

import React, { useState } from 'react';

type Props = {
    onSelect: (price: number) => void;
};

const PricingControl: React.FC<Props> = ({ onSelect }) => {
    const [selectedPrice, setSelectedPrice] = useState<number | null>(null);
    const prices = [60, 54, 48, 36];

    return (
        <div style={{ display: 'flex', marginBottom: '20px' }}>
            {prices.map((price) => (
                <button
                    key={price}
                    onClick={() => {
                        setSelectedPrice(price);
                        onSelect(price);
                    }}
                    disabled={selectedPrice !== null && price > selectedPrice}
                    style={{
                        flex: 1,
                        padding: '10px',
                        backgroundColor: (selectedPrice !== null && price > selectedPrice) ? '#E0E0E0' : 'green',
                        color: 'white',
                        border: 'none',
                        borderRadius: '5px',
                        cursor: (selectedPrice !== null && price > selectedPrice) ? 'default' : 'pointer',
                        margin: '0 5px',
                        transition: 'background-color 0.3s'
                    }}
                    onMouseOver={(e) => {
                        if (!(selectedPrice !== null && price > selectedPrice)) {
                            e.currentTarget.style.backgroundColor = '#4CAF50';
                        }
                    }}
                    onMouseOut={(e) => {
                        if (!(selectedPrice !== null && price > selectedPrice)) {
                            e.currentTarget.style.backgroundColor = 'green';
                        }
                    }}
                >
                    NT${price}
                </button>
            ))}
        </div>
    );
};

export default PricingControl;
  • CSS
    • onMouseOveronMouseOut :當滑鼠靠近和沒靠近的時候,按鈕背景顏色變化。
    • style :稍微修改一下呈現方式。
      • **flex:**這是 CSS Flexible Box Layout,簡稱 Flexbox 的一部分,主要用於在複雜的佈局和對齊中設定和調整子元素的尺寸和順序。說真的,這個真的很推!!!
      • **border:**元素的邊框,
        • :包括 border-width, border-style, 和 border-color,例如:border: 1px solid red
      • borderRadius:邊寬圓角的角半徑
      • cursor:滑鼠放在元素上面後,滑鼠的類型。
        • :如 pointer(表示可點擊)、wait(表示等待)、text(表示可編輯的文字)還有很多。
      • **margin:**元素的外邊距,可以跟其他元素分開。
      • **transition:**指定時間內進行變化。
  • disabled :來確保不會有點低價賤賣後還可以選高價格~

Step3. 修改一下 App.tsx

// App.tsx

import React, { useState } from 'react';
import LineChart from './Component/LineChart';
import PricingControl from './Component/PricingControl';
import { SaleRecord } from './Component/types';
import { simulateSales } from './Component/salesLogic';
import SalesTable from './Component/SalesTable';

const INITIAL_STOCK = 2000;

const App: React.FC = () => {
    const [salesData, setSalesData] = useState<SaleRecord[]>([
        { week: 1, price: 60, initialStock: INITIAL_STOCK, sales: 0, remainingStock: INITIAL_STOCK, revenue: 0, accumulatedRevenue: 0 },
    ]);
    const handlePricingSelect = (price: number) => {
        const currentWeek = salesData.length;
        const previousRecord = salesData[currentWeek - 1];
        const sales = simulateSales(previousRecord.remainingStock);
        const newRemainingStock = previousRecord.remainingStock - sales;
        const revenue = sales * price;
        const accumulatedRevenue = previousRecord.accumulatedRevenue + revenue;

        const record: SaleRecord = {
            week: currentWeek + 1,
            price,
            initialStock: INITIAL_STOCK,
            sales,
            remainingStock: newRemainingStock,
            revenue,
            accumulatedRevenue,
        };

        setSalesData([...salesData, record]);
    };

    return (
        <div className="App" style={{ textAlign: 'center' }}>
            <h1 style={{ backgroundColor: 'green', color: 'white', padding: '20px', borderRadius: '5px' }}>
                Retail Simulation
            </h1>
            <LineChart data={salesData} />
            <PricingControl onSelect={handlePricingSelect} />
            <SalesTable data={salesData} />
        </div>
    );
};

export default App;
  • const handlePricingSelect :依據 types 的宣告型別 SaleRecord 來進行資料維護。
  • return 的部分稍微修正一下排序跟標題修正一下~

大概就會像這樣子。
https://ithelp.ithome.com.tw/upload/images/20231009/2016310745MC9Kz5eE.png
https://ithelp.ithome.com.tw/upload/images/20231009/201631077V71p5jArq.png


上一篇
[Day 24] TypeScript 零售業者模擬演練 II
下一篇
[Day 26] TypeScript 零售業者模擬演練 IV
系列文
TypeScript 啟動!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言