欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot+React實(shí)現(xiàn)計(jì)算個(gè)人所得稅

 更新時(shí)間:2023年09月07日 14:43:48   作者:葡萄城技術(shù)團(tuán)隊(duì)  
本文將以個(gè)人所得稅的計(jì)算為例,使用React+SpringBoot+GcExcel來(lái)實(shí)現(xiàn)這一功能,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下

前言

在報(bào)表數(shù)據(jù)處理中,Excel公式擁有強(qiáng)大而多樣的功能,廣泛應(yīng)用于各個(gè)業(yè)務(wù)領(lǐng)域。無(wú)論是投資收益計(jì)算、財(cái)務(wù)報(bào)表編制還是保險(xiǎn)收益估算,Excel公式都扮演著不可或缺的角色。傳統(tǒng)的做法是直接依賴Excel來(lái)實(shí)現(xiàn)復(fù)雜的業(yè)務(wù)邏輯,并生成相應(yīng)的Excel文件。因此只需在預(yù)設(shè)位置輸入相應(yīng)參數(shù),Excel公式即可被激活,迅速計(jì)算并呈現(xiàn)結(jié)果。正因如此,在這類場(chǎng)景中,企業(yè)積累了大量用于計(jì)算的Excel文件,它們已經(jīng)成為了無(wú)價(jià)的財(cái)富。

然而,傳統(tǒng)的Excel文件方式存在難以管理和數(shù)據(jù)不安全的缺點(diǎn)。為了解決這些問(wèn)題,可以采用B/S架構(gòu)+Excel組件庫(kù)的方式。

本文將以個(gè)人所得稅的計(jì)算為例,使用React+Spring Boot+GcExcel來(lái)實(shí)現(xiàn)。首先準(zhǔn)備好Excel文件,按照國(guó)家稅務(wù)總局提供的個(gè)稅計(jì)算頁(yè)面進(jìn)行創(chuàng)建。

個(gè)人所得稅的收入類型有8種:

  • 工資薪金所得
  • 年終獎(jiǎng)所得
  • 勞務(wù)報(bào)酬所得
  • 個(gè)體工商戶、生產(chǎn)經(jīng)營(yíng)所得
  • 酬勞所得
  • 偶然所得
  • 利息、股息、紅利所得
  • 財(cái)產(chǎn)轉(zhuǎn)讓所得

其中,工資薪金所得最為復(fù)雜,包括社會(huì)保險(xiǎn)和專項(xiàng)扣除。每種類型的計(jì)稅方式都不同,為了便于理解,我們?yōu)槊總€(gè)類型創(chuàng)建了一個(gè)工作表進(jìn)行計(jì)算。

以下是準(zhǔn)備好的Excel文件,其中藍(lán)色部分為需要輸入?yún)?shù)的單元格,其他單元格將自動(dòng)計(jì)算。

完成準(zhǔn)備工作后,下面開(kāi)始前后端工程的搭建。

實(shí)踐

前端 React

創(chuàng)建React工程

新建一個(gè)文件夾,如TaxCalculator,進(jìn)入文件夾,在資源管理器的地址欄里輸入cmd,然后回車,打開(kāi)命令行窗口。使用下面的代碼創(chuàng)建名為client-app的react app。

npx create-react-app salary-client

進(jìn)入剛創(chuàng)建的salary-client文件夾,使用IDE,比如VisualStudio Code打開(kāi)文件夾。

界面部分

個(gè)人所得稅涉及的收入類型一共有8種,其中(“酬勞所得”,“偶然所得”,“利息、股息、紅利所得”,“財(cái)產(chǎn)轉(zhuǎn)讓所得”)四種的計(jì)算方式接近,UI布局相似,借助React的component特性,最終需要提供5種表單界面。

如下圖所示:

為了讓UI看起來(lái)更好看一些,可以先引入一個(gè)UI框架,這里我們使用了MUI。

npm install @mui/material @emotion/react @emotion/styled

首先,更新Src/App.js的代碼,其中添加了DarkMode的Theme, 代碼如下:

import './App.css';
import { ThemeProvider } from '@emotion/react';
import { createTheme } from '@mui/material';
import { FormContainer } from './Component/FormContainer';
const darkTheme = createTheme({
  palette: {
    mode: 'dark',
  },
});
function App() {
  return (
    <ThemeProvider theme={darkTheme}>
      <div className="App-header">
        <h2>個(gè)人所得稅計(jì)算器</h2>
        <FormContainer></FormContainer>
      </div>
    </ThemeProvider>
  );
}
export default App;

可以看到,App.js中引用了FormContainer,下來(lái)添加 ./Component/FormContainer.js。

FormContainer主要是提供一個(gè)Selector,讓用戶選擇收入類型,根據(jù)選擇的類型渲染不同的組件。

import React, { useState } from 'react';
import { SalaryIncome } from "./SalaryIncome"
import { NativeSelect, FormControl } from '@mui/material';
import { BounsIncome } from './BounsIncome';
import { CommercialIncome } from './CommercialIncome';
import { LaborIncome } from './LaborIncome';
import { OtherIncome } from './OtherIncome';
export const FormContainer = () => {
    const [calcType, setCalcType] = useState("工資薪金所得");
    const GetIncomeControl = () => {
        switch (calcType) {
            case "工資薪金所得":
                return <SalaryIncome calcType={calcType}></SalaryIncome>;
            case "年終獎(jiǎng)所得":
                return <BounsIncome calcType={calcType}></BounsIncome>;
            case "勞務(wù)報(bào)酬所得":
                return <LaborIncome calcType={calcType}></LaborIncome>;
            case "個(gè)體工商戶、生產(chǎn)經(jīng)營(yíng)所得":
                return <CommercialIncome calcType={calcType}></CommercialIncome>;
            default:
                return <OtherIncome calcType={calcType}></OtherIncome>;
        }
    }
    return (
        <div style={{ width: "60vw", marginTop: "5vh" }}>
            <FormControl fullWidth sx={{ marginBottom: 2 }}>
                <NativeSelect labelId="demo-simple-select-label" id="demo-simple-select"
                    value={calcType} label="類型" onChange={e => setCalcType(e.target.value)}                    >
                    <option value="工資薪金所得">工資薪金所得</option>
                    <option value="年終獎(jiǎng)所得">年終獎(jiǎng)所得</option>
                    <option Item value="勞務(wù)報(bào)酬所得">勞務(wù)報(bào)酬所得</option>
                    <option value="個(gè)體工商戶、生產(chǎn)經(jīng)營(yíng)所得">個(gè)體工商戶、生產(chǎn)經(jīng)營(yíng)所得</option>
                    <option value="酬勞所得">酬勞所得</option>
                    <option value="偶然所得">偶然所得</option>
                    <option value="利息、股息、紅利所得">利息、股息、紅利所得</option>
                </NativeSelect>
            </FormControl>
            {GetIncomeControl()}
        </div>);
}

例如:\<SalaryIncome calcType={calcType}\>\</SalaryIncome\>; 同時(shí)會(huì)將calcType傳遞進(jìn)去。

接下來(lái),分別創(chuàng)建幾個(gè)xxxIncome組件。

1.工資薪金所得 SalaryIncome.js

import React, { useState } from 'react';
import { TextField, Button, Stack } from '@mui/material';
import axios from 'axios';
export const SalaryIncome = (props) => {
    const [income, setIncome] = useState("");
    const [insurance, setInsurance] = useState("");
    const [childEdu, setChildEdu] = useState("");
    const [selfEdu, setSelfEdu] = useState("");
    const [treatment, setTreatment] = useState("");
    const [loans, setLoans] = useState("");
    const [rent, setRent] = useState("");
    const [elder, setElder] = useState("");
    const [taxableIncome, setTaxableIncome] = useState("");
    const [taxRate, setTaxRate] = useState("");
    const [deduction, setDeduction] = useState("");
    const [tax, setTax] = useState("");
    const [takeHomeSalary, setTakeHomeSalary] = useState("");
    async function calculateTax(event) {
        event.preventDefault();
        let res = await axios.post("api/calcPersonTax", {
            calcType: props.calcType,
            income: income,
            insurance: insurance,
            childEdu: childEdu,
            selfEdu: selfEdu,
            treatment: treatment,
            loans: loans,
            rent: rent,
            elder: elder,
        });
        if (res != null) {
            let data = res.data;
            setTaxableIncome(data.taxableIncome);
            setTaxRate(data.taxRate);
            setDeduction(data.deduction);
            setTax(data.tax);
            setTakeHomeSalary(data.takeHomeSalary);
        }
    }
    function reset(event) {
        event.preventDefault();
        setIncome("");
        setInsurance("");
        setChildEdu("");
        setSelfEdu("");
        setTreatment("");
        setLoans("");
        setRent("");
        setElder("");
        setTaxableIncome("");
        setTaxRate("");
        setDeduction("");
        setTax("");
        setTakeHomeSalary("");
    }
    return (
        <div>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='primary'
                    label="稅前工資" onChange={e => setIncome(e.target.value)}
                    value={income} fullWidth required size="small"/>
                <TextField type="text" variant='outlined' color='secondary'
                    label="社會(huì)保險(xiǎn)/公積金" onChange={e => setInsurance(e.target.value)}
                    value={insurance} fullWidth size="small"/>
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary'
                    label="子女教育專項(xiàng)扣除" onChange={e => setChildEdu(e.target.value)}
                    value={childEdu} fullWidth size="small"/>
                <TextField type="text" variant='outlined' color='secondary'
                    label="繼續(xù)教育專項(xiàng)扣除" onChange={e => setSelfEdu(e.target.value)}
                    value={selfEdu} fullWidth size="small"/>
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary'
                    label="大病醫(yī)療專項(xiàng)扣除" onChange={e => setTreatment(e.target.value)}
                    value={treatment} fullWidth size="small"/>
                <TextField type="text" variant='outlined' color='secondary'
                    label="住房貸款利息專項(xiàng)扣除" onChange={e => setLoans(e.target.value)}
                    value={loans} fullWidth size="small"/>
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary'
                    label="住房租金專項(xiàng)扣除" onChange={e => setRent(e.target.value)}
                    value={rent} fullWidth size="small"/>
                <TextField type="text" variant='outlined' color='secondary'
                    label="贍養(yǎng)老人專項(xiàng)扣除" onChange={e => setElder(e.target.value)}
                    value={elder} fullWidth size="small"/>
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary'
                    label="起征點(diǎn)" value="5000 元/月" fullWidth disabled size="small"/>
            </Stack>
            <hr></hr>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <Button variant="outlined" color="primary" onClick={calculateTax} fullWidth size="large">計(jì)算</Button>
                <Button variant="outlined" color="secondary" onClick={reset} fullWidth size="large">重置</Button>
            </Stack>
            <hr></hr>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary'
                    label="應(yīng)納稅所得額" value={taxableIncome} fullWidth disabled size="small"/>
                <TextField type="text" variant='outlined' color='secondary'
                    label="稅率" value={taxRate} fullWidth disabled size="small"/>
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary'
                    label="速算扣除數(shù)" value={deduction} fullWidth disabled size="small"/>
                <TextField type="text" variant='outlined' color='secondary'
                    label="應(yīng)納稅額" value={tax} fullWidth disabled size="small"/>
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary'
                    label="稅后工資" value={takeHomeSalary} fullWidth disabled size="small"/>
            </Stack>
        </div>
    )
}

2.年終獎(jiǎng)金所得 BounsIncome.js

import React, { useState } from 'react';
import { TextField, Button, Stack } from '@mui/material';
import axios from 'axios';
export const BounsIncome = (props) => {
    const [income, setIncome] = useState("");
    const [taxableIncome, setTaxableIncome] = useState("");
    const [taxRate, setTaxRate] = useState("");
    const [deduction, setDeduction] = useState("");
    const [monthlyWage, setMonthlyWage] = useState("");
    const [tax, setTax] = useState("");
    const [takeHomeSalary, setTakeHomeSalary] = useState("");
    async function calculateTax(event) {
        event.preventDefault();
        let res = await axios.post("api/calcPersonTax", {
            calcType: props.calcType,
            income: income,
        });
        if (res != null) {
            let data = res.data;
            setTaxableIncome(data.taxableIncome);
            setTaxRate(data.taxRate);
            setDeduction(data.deduction);
            setMonthlyWage(data.monthlyWage);
            setTax(data.tax);
            setTakeHomeSalary(data.takeHomeSalary);
        }
    }
    function reset(event) {
        event.preventDefault();
        setIncome("");
        setTaxableIncome("");
        setTaxRate("");
        setDeduction("");
        setMonthlyWage("");
        setTax("");
        setTakeHomeSalary("");
    }
    return (
        <div>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='primary' size="small"
                    label="稅前工資" onChange={e => setIncome(e.target.value)}
                    value={income} fullWidth required />
            </Stack>
            <hr></hr>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <Button variant="outlined" color="primary" onClick={calculateTax} fullWidth size="large">計(jì)算</Button>
                <Button variant="outlined" color="secondary" onClick={reset} fullWidth size="large">重置</Button>
            </Stack>
            <hr></hr>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="應(yīng)納稅所得額" value={taxableIncome} fullWidth disabled />
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="稅率" value={taxRate} fullWidth disabled />
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="速算扣除數(shù)" value={deduction} fullWidth disabled />
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="平均每月工資" value={monthlyWage} fullWidth disabled />
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="應(yīng)納稅額" value={tax} fullWidth disabled />
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="稅后工資" value={takeHomeSalary} fullWidth disabled />
            </Stack>
        </div>
    )
}

3.勞務(wù)報(bào)酬所得 LaborIncome.js

import React, { useState } from 'react';
import { TextField, Button, Stack } from '@mui/material';
import axios from 'axios';
export const LaborIncome = (props) => {
    const [income, setIncome] = useState("");
    const [taxableIncome, setTaxableIncome] = useState("");
    const [taxRate, setTaxRate] = useState("");
    const [deduction, setDeduction] = useState("");
    const [nonTaxablePart, setNonTaxablePart] = useState("");
    const [tax, setTax] = useState("");
    const [takeHomeSalary, setTakeHomeSalary] = useState("");
    async function calculateTax(event) {
        event.preventDefault();
        let res = await axios.post("api/calcPersonTax", {
            calcType: props.calcType,
            income: income,
        });
        if (res != null) {
            let data = res.data;
            setTaxableIncome(data.taxableIncome);
            setTaxRate(data.taxRate);
            setDeduction(data.deduction);
            setNonTaxablePart(data.nonTaxablePart);
            setTax(data.tax);
            setTakeHomeSalary(data.takeHomeSalary);
        }
    }
    function reset(event) {
        event.preventDefault();
        setIncome("");
        setTaxableIncome("");
        setTaxRate("");
        setDeduction("");
        setNonTaxablePart("");
        setTax("");
        setTakeHomeSalary("");
    }
    return (
        <div>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='primary' size="small"
                    label="稅前工資" onChange={e => setIncome(e.target.value)}
                    value={income} fullWidth required />
            </Stack>
            <hr></hr>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <Button variant="outlined" color="primary" onClick={calculateTax} fullWidth size="large">計(jì)算</Button>
                <Button variant="outlined" color="secondary" onClick={reset} fullWidth size="large">重置</Button>
            </Stack>
            <hr></hr>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="應(yīng)納稅所得額" value={taxableIncome} fullWidth disabled />
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="稅率" value={taxRate} fullWidth disabled />
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="速算扣除數(shù)" value={deduction} fullWidth disabled />
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="減除費(fèi)用" value={nonTaxablePart} fullWidth disabled />
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="應(yīng)納稅額" value={tax} fullWidth disabled />
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="稅后工資" value={takeHomeSalary} fullWidth disabled />
            </Stack>
        </div>
    )
}

4.個(gè)體工商戶、生產(chǎn)經(jīng)營(yíng)所得 CommercialIncome.js

import React, { useState } from 'react';
import { TextField, Button, Stack } from '@mui/material';
import axios from 'axios';
export const CommercialIncome = (props) => {
    const [income, setIncome] = useState("");
    const [taxableIncome, setTaxableIncome] = useState("");
    const [taxRate, setTaxRate] = useState("");
    const [deduction, setDeduction] = useState("");
    const [tax, setTax] = useState("");
    const [takeHomeSalary, setTakeHomeSalary] = useState("");
    async function calculateTax(event) {
        event.preventDefault();
        let res = await axios.post("api/calcPersonTax", {
            calcType: props.calcType,
            income: income,
        });
        if (res != null) {
            let data = res.data;
            setTaxableIncome(data.taxableIncome);
            setTaxRate(data.taxRate);
            setDeduction(data.deduction);
            setTax(data.tax);
            setTakeHomeSalary(data.takeHomeSalary);
        }
    }
    function reset(event) {
        event.preventDefault();
        setIncome("");
        setTaxableIncome("");
        setTaxRate("");
        setDeduction("");
        setTax("");
        setTakeHomeSalary("");
    }
    return (
        <div>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='primary' size="small"
                    label="稅前工資" onChange={e => setIncome(e.target.value)}
                    value={income} fullWidth required />
            </Stack>
            <hr></hr>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <Button variant="outlined" color="primary" onClick={calculateTax} fullWidth size="large">計(jì)算</Button>
                <Button variant="outlined" color="secondary" onClick={reset} fullWidth size="large">重置</Button>
            </Stack>
            <hr></hr>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="應(yīng)納稅所得額" value={taxableIncome} fullWidth disabled />
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="稅率" value={taxRate} fullWidth disabled />
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="速算扣除數(shù)" value={deduction} fullWidth disabled />
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="應(yīng)納稅額" value={tax} fullWidth disabled />
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="稅后工資" value={takeHomeSalary} fullWidth disabled />
            </Stack>
        </div>
    )
}

5.余下四種類型 OtherIncome.js

import React, { useState } from 'react';
import { TextField, Button, Stack } from '@mui/material';
import axios from 'axios';
export const OtherIncome = (props) => {
    const [income, setIncome] = useState("");
    const [taxableIncome, setTaxableIncome] = useState("");
    const [taxRate, setTaxRate] = useState("");
    const [tax, setTax] = useState("");
    const [takeHomeSalary, setTakeHomeSalary] = useState("");
    async function calculateTax(event) {
        event.preventDefault();
        let res = await axios.post("api/calcPersonTax", {
            calcType: props.calcType,
            income: income,
        });
        if (res != null) {
            let data = res.data;
            setTaxableIncome(data.taxableIncome);
            setTaxRate(data.taxRate);
            setTax(data.tax);
            setTakeHomeSalary(data.takeHomeSalary);
        }
    }
    function reset(event) {
        event.preventDefault();
        setIncome("");
        setTaxableIncome("");
        setTaxRate("");
        setTax("");
        setTakeHomeSalary("");
    }
    return (
        <div>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='primary' size="small"
                    label={props.calcType} onChange={e => setIncome(e.target.value)}
                    value={income} fullWidth required />
            </Stack>
            <hr></hr>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <Button variant="outlined" color="primary" onClick={calculateTax} fullWidth size="large">計(jì)算</Button>
                <Button variant="outlined" color="secondary" onClick={reset} fullWidth size="large">重置</Button>
            </Stack>
            <hr></hr>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="應(yīng)納稅所得額" value={taxableIncome} fullWidth disabled />
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="稅率" value={taxRate} fullWidth disabled />
            </Stack>
            <Stack spacing={2} direction="row" sx={{ marginBottom: 2 }}>
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="應(yīng)納稅額" value={tax} fullWidth disabled />
                <TextField type="text" variant='outlined' color='secondary' size="small"
                    label="稅后工資" value={takeHomeSalary} fullWidth disabled />
            </Stack>
        </div>
    )
}

此時(shí),完成UI部分后,可以嘗試運(yùn)行起來(lái),效果如下:

//通過(guò)代碼運(yùn)行React app
npm start

可以試著填一些數(shù)據(jù),但是當(dāng)我們點(diǎn)擊計(jì)算時(shí)會(huì)報(bào)錯(cuò),這是因?yàn)榉?wù)端還沒(méi)有準(zhǔn)備好。

前端請(qǐng)求部分

熟悉Axios的同學(xué)可以跳過(guò)這部分,前面的代碼里,已經(jīng)給出了Axois發(fā)送請(qǐng)求的代碼。

可以看到無(wú)論是哪一種類型的組件,請(qǐng)求都發(fā)送到了相同的url("api/calcPersonTax"),以SalaryIncome為例,代碼如下:

async function calculateTax(event) {
        event.preventDefault();
        let res = await axios.post("api/calcPersonTax", {
            calcType: props.calcType,
            income: income,
            insurance: insurance,
            childEdu: childEdu,
            selfEdu: selfEdu,
            treatment: treatment,
            loans: loans,
            rent: rent,
            elder: elder,
        });
        if (res != null) {
            let data = res.data;
            setTaxableIncome(data.taxableIncome);
            setTaxRate(data.taxRate);
            setDeduction(data.deduction);
            setTax(data.tax);
            setTakeHomeSalary(data.takeHomeSalary);
        }
    }

可以看到,整個(gè)請(qǐng)求變得非常簡(jiǎn)單,主要是把state的值取出來(lái),通過(guò)post請(qǐng)求發(fā)送到服務(wù)端,然后根據(jù)返回值,把數(shù)據(jù)重新設(shè)給state,這樣就完成UI數(shù)據(jù)的更新了。

配置請(qǐng)求轉(zhuǎn)發(fā)中間件

我們?cè)谡?qǐng)求時(shí)訪問(wèn)的是相對(duì)地址,React本身有一個(gè)nodeJS,默認(rèn)的端口是3000,而Spring Boot的默認(rèn)端口是8080。前端直接訪問(wèn)會(huì)有跨域的問(wèn)題,因此我們要做一個(gè)代理的配置。

在src文件夾下面添加文件,名為setupProxy.js,代碼如下:

const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://localhost:8080',
      changeOrigin: true,
    })
  );
};

服務(wù)端 Spring Boot

創(chuàng)建工程及添加依賴

使用IDEA創(chuàng)建一個(gè)Spring Boot工程,如果使用的是社區(qū)(community)版本,不能直接創(chuàng)建Spring Boot項(xiàng)目,那可以先創(chuàng)建一個(gè)空項(xiàng)目,idea創(chuàng)建project的過(guò)程,就跳過(guò)了,這里我們以創(chuàng)建了一個(gè)gradle項(xiàng)目為例。

plugins {
    id 'org.springframework.boot' version '3.0.0'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
    id 'war'
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
    mavenCentral()
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'com.grapecity.documents:gcexcel:6.2.0'
    implementation 'javax.json:javax.json-api:1.1.4'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
    testImplementation('org.springframework.boot:spring-boot-starter-test')
}
test {
    useJUnitPlatform()
}

在dependencies 中,我們除了依賴Spring Boot之外,還添加了GcExcel的依賴,后面導(dǎo)出時(shí)會(huì)用到GcExcel,目前的版本是6.2.0。

添加API

在Application類上,添加屬性 @RequestMapping("/api").,并添加 calcPersonTax API。

@Spring BootApplication
@RestController
@RequestMapping("/api")
public class SalaryTaxCalculator {
    public static void main(String[] args) {
        SpringApplication.run(SalaryTaxCalculator.class, args);
    }
    @PostMapping("/calcPersonTax")
    public CalcResult calcTax(@RequestBody CalcParameter par) {
        Workbook workbook = new Workbook();
        workbook.open(GetResourcePath());
        return CalcInternal(workbook, par);
    }
    private String GetResourcePath(){
        return Objects.requireNonNull(SalaryTaxCalculator.class.getClassLoader().getResource("PersonalTaxCalcEngine.xlsx")).getPath();
    }
    private CalcResult CalcInternal(Workbook workbook, CalcParameter par) {
        //todo
    }
}

可以看到在CalcInternal方法內(nèi),我們使用GcExcel,根據(jù)calcType來(lái)判斷使用哪一個(gè)sheet來(lái)進(jìn)行計(jì)算。對(duì)不同Sheet只需要通過(guò)GcExcel設(shè)值,并從特定的格子里取值即可。

同時(shí),我們還需要?jiǎng)?chuàng)建兩個(gè)類,CalcParameter和CalcResult。CalcParameter用于從request中把post的data解析出來(lái),CalcResult用于在response中返回的數(shù)據(jù)。

CalcParameter:

public class CalcParameter {
    public String calcType;
    public double income;
    public double insurance;
    public double childEdu;
    public double selfEdu;
    public double treatment;
    public double loans;
    public double rent;
    public double elder;
}

CalcResult:

public class CalcResult {
    public double taxableIncome;
    public double taxRate;
    public double deduction;
    public double tax;
    public double takeHomeSalary;
    public double monthlyWage;
    public double nonTaxablePart;
}

使用GcExcel完成公式計(jì)算

前面我們定義了 CalcInternal,在 CalcInternal 中,我們需要使用GcExcel來(lái)完成公式計(jì)算。

GcExcel的公式計(jì)算是自動(dòng)完成的,我們使用workbook打開(kāi)Excel文件后,只需要set相關(guān)的value。之后在取值時(shí),GcExcel會(huì)自動(dòng)計(jì)算響應(yīng)公式的值。

private CalcResult CalcInternal(Workbook workbook, CalcParameter par) {
        var result = new CalcResult();
        var sheet = workbook.getWorksheets().get(par.calcType);
        switch (par.calcType) {
            case "工資薪金所得" -> {
                sheet.getRange("B1").setValue(par.income);
                sheet.getRange("D1").setValue(par.insurance);
                sheet.getRange("B2").setValue(par.childEdu);
                sheet.getRange("D2").setValue(par.selfEdu);
                sheet.getRange("B3").setValue(par.treatment);
                sheet.getRange("D3").setValue(par.loans);
                sheet.getRange("B4").setValue(par.rent);
                sheet.getRange("D4").setValue(par.elder);
                result.taxableIncome = (double) sheet.getRange("B9").getValue();
                result.taxRate = (double) sheet.getRange("D9").getValue();
                result.deduction = (double) sheet.getRange("B10").getValue();
                result.tax = (double) sheet.getRange("D10").getValue();
                result.takeHomeSalary = (double) sheet.getRange("B11").getValue();
            }
            case "年終獎(jiǎng)所得" -> {
                sheet.getRange("B1").setValue(par.income);
                result.taxableIncome = (double) sheet.getRange("B3").getValue();
                result.taxRate = (double) sheet.getRange("D3").getValue();
                result.deduction = (double) sheet.getRange("B4").getValue();
                result.monthlyWage = (double) sheet.getRange("D4").getValue();
                result.tax = (double) sheet.getRange("B5").getValue();
                result.takeHomeSalary = (double) sheet.getRange("D5").getValue();
            }
            case "勞務(wù)報(bào)酬所得" -> {
                sheet.getRange("B1").setValue(par.income);
                result.taxableIncome = (double) sheet.getRange("B3").getValue();
                result.taxRate = (double) sheet.getRange("D3").getValue();
                result.deduction = (double) sheet.getRange("B4").getValue();
                result.nonTaxablePart = (double) sheet.getRange("D4").getValue();
                result.tax = (double) sheet.getRange("B5").getValue();
                result.takeHomeSalary = (double) sheet.getRange("D5").getValue();
            }
            case "個(gè)體工商戶、生產(chǎn)經(jīng)營(yíng)所得" -> {
                sheet.getRange("B1").setValue(par.income);
                result.taxableIncome = (double) sheet.getRange("B3").getValue();
                result.taxRate = (double) sheet.getRange("D3").getValue();
                result.deduction = (double) sheet.getRange("B4").getValue();
                result.tax = (double) sheet.getRange("D4").getValue();
                result.takeHomeSalary = (double) sheet.getRange("B5").getValue();
            }
            default -> {
                sheet.getRange("B1").setValue(par.income);
                result.taxableIncome = (double) sheet.getRange("B3").getValue();
                result.taxRate = (double) sheet.getRange("D3").getValue();
                result.tax = (double) sheet.getRange("B4").getValue();
                result.takeHomeSalary = (double) sheet.getRange("D4").getValue();
            }
        }
        return result;
    }

這樣就完成了服務(wù)端的代碼。

最終效果

我們可以使用工資薪金所得試驗(yàn)一下,可以看到數(shù)據(jù)被計(jì)算出來(lái)了。因?yàn)槟康氖菫榱朔窒矸?wù)端公式計(jì)算的方案,所以計(jì)算的結(jié)果是否正確,就不做細(xì)致考慮。

總結(jié)

個(gè)稅計(jì)算的場(chǎng)景并不復(fù)雜,主要是通過(guò)Excel完成公式計(jì)算即可,在服務(wù)端使用GcExcel可以大幅度降低前后端的開(kāi)發(fā)難度,系統(tǒng)的搭建過(guò)程可以完全不需要考慮計(jì)算的邏輯。

在實(shí)際的公式計(jì)算場(chǎng)景中,可能往往會(huì)比個(gè)稅計(jì)算的場(chǎng)景復(fù)雜,借助GcExcel這樣Excel組件庫(kù),可以很容易的把已有的Excel文件遷移到線上,提高工作效率。

另外,本文中分享的代碼并不是最符合實(shí)際工作中的要求,讀者還可以從以下角度去優(yōu)化自己的代碼。

  • 收入類型可以抽成枚舉,這樣維護(hù)和使用起來(lái)更容易。
  • 目前每一個(gè)react組件里的冗余度還不低,還可以繼續(xù)抽象組件,避免重復(fù)寫代碼。
  • 在服務(wù)端,因?yàn)楣接?jì)算的邏輯是不會(huì)變的,在實(shí)際場(chǎng)景中,也有可能同一時(shí)間要加載復(fù)數(shù)個(gè)Excel文件,可以考慮把workbook常駐內(nèi)存,來(lái)提高性能。

以上就是SpringBoot+React實(shí)現(xiàn)計(jì)算個(gè)人所得稅的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot Reac計(jì)算個(gè)人所得稅的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring如何通過(guò)@Lazy注解解決構(gòu)造方法循環(huán)依賴問(wèn)題

    Spring如何通過(guò)@Lazy注解解決構(gòu)造方法循環(huán)依賴問(wèn)題

    循環(huán)依賴其實(shí)就是循環(huán)引用,也就是兩個(gè)或則兩個(gè)以上的bean互相持有對(duì)方,最終形成閉環(huán),這篇文章主要給大家介紹了關(guān)于Spring如何通過(guò)@Lazy注解解決構(gòu)造方法循環(huán)依賴問(wèn)題的相關(guān)資料,需要的朋友可以參考下
    2023-03-03
  • Java transient關(guān)鍵字使用小記

    Java transient關(guān)鍵字使用小記

    這篇文章主要為大家詳細(xì)介紹了Java transient關(guān)鍵字的使用方法,感興趣的小伙伴們可以參考一下
    2016-06-06
  • java進(jìn)制轉(zhuǎn)換工具類實(shí)現(xiàn)減少參數(shù)長(zhǎng)度

    java進(jìn)制轉(zhuǎn)換工具類實(shí)現(xiàn)減少參數(shù)長(zhǎng)度

    這篇文章主要為大家介紹了java進(jìn)制轉(zhuǎn)換工具類實(shí)現(xiàn)減少參數(shù)長(zhǎng)度示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • 詳解java如何調(diào)用代理ip

    詳解java如何調(diào)用代理ip

    在進(jìn)行網(wǎng)絡(luò)爬蟲(chóng)、數(shù)據(jù)采集或是訪問(wèn)網(wǎng)站時(shí),使用代理IP顯得尤為重要,本文將深入探討如何在Java中調(diào)用代理IP,感興趣的小伙伴可以了解一下
    2024-11-11
  • Java中數(shù)組的常見(jiàn)操作合集

    Java中數(shù)組的常見(jiàn)操作合集

    這篇文章主要為大家詳細(xì)介紹了Java中數(shù)組的一些常見(jiàn)操作,例如:數(shù)組遍歷、數(shù)組獲取最大值元素、數(shù)組反轉(zhuǎn)等,感興趣的小伙伴可以了解一下
    2022-10-10
  • IDEA中Maven爆紅以及依賴下載失敗的最全解決方案

    IDEA中Maven爆紅以及依賴下載失敗的最全解決方案

    這篇文章主要介紹了IDEA中Maven爆紅,依賴下載失敗的最全解決方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08
  • Java?Process中waitFor()的問(wèn)題詳解

    Java?Process中waitFor()的問(wèn)題詳解

    這篇文章主要給大家介紹了關(guān)于Java?Process中waitFor()問(wèn)題的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-12-12
  • 使用Apache Ignite實(shí)現(xiàn)Java數(shù)據(jù)網(wǎng)格

    使用Apache Ignite實(shí)現(xiàn)Java數(shù)據(jù)網(wǎng)格

    今天我們來(lái)探討如何使用Apache Ignite來(lái)實(shí)現(xiàn)Java數(shù)據(jù)網(wǎng)格,Apache Ignite是一個(gè)高性能的內(nèi)存計(jì)算平臺(tái),它提供了分布式緩存、數(shù)據(jù)網(wǎng)格和計(jì)算功能,可以顯著提高大規(guī)模應(yīng)用的數(shù)據(jù)處理性能,感興趣的小伙伴跟著小編一起來(lái)看看吧
    2024-08-08
  • Java從入門到起飛之變量與運(yùn)算符詳解

    Java從入門到起飛之變量與運(yùn)算符詳解

    這篇文章主要介紹了Java編程語(yǔ)言中的關(guān)鍵字、標(biāo)識(shí)符、變量、基本數(shù)據(jù)類型以及運(yùn)算符等基本概念和用法,它涵蓋了變量聲明、賦值、類型轉(zhuǎn)換、字符串操作以及運(yùn)算符優(yōu)先級(jí)等內(nèi)容,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-03-03
  • JS實(shí)現(xiàn)冒泡排序,插入排序和快速排序并排序輸出

    JS實(shí)現(xiàn)冒泡排序,插入排序和快速排序并排序輸出

    這篇文章主要介紹了JS實(shí)現(xiàn)冒泡排序,插入排序和快速排序并從input文本框中獲取內(nèi)容進(jìn)行排序輸出,需要的朋友可以參考下
    2015-07-07

最新評(píng)論