完成财务统计页的布局

main
wuyize 2023-01-04 18:06:32 +08:00
parent 0a522d1fe6
commit 1206b23280
43 changed files with 2063 additions and 3 deletions

1557
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@ant-design/pro-components": "^2.3.47", "@ant-design/pro-components": "^2.3.47",
"@antv/l7": "^2.13.0",
"@reduxjs/toolkit": "^1.9.1", "@reduxjs/toolkit": "^1.9.1",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
@ -14,6 +15,9 @@
"@types/react-dom": "^18.0.9", "@types/react-dom": "^18.0.9",
"antd": "^5.1.0", "antd": "^5.1.0",
"axios": "^1.2.1", "axios": "^1.2.1",
"echarts": "^5.4.1",
"echarts-china-counties-js": "^1.0.1",
"echarts-for-react": "^3.0.2",
"history": "^5.3.0", "history": "^5.3.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"id":"820001","type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[["@@LADC^umZ@DONWE@DALBBF@H@DFBBTC"],["@@P@LC@AGM@OECMBABBTCD@DDH"]],"encodeOffsets":[[[116285,22746]],[[116303,22746]]]},"properties":{"cp":[113.552965,22.207882],"name":"花地玛堂区","childNum":2}},{"id":"820002","type":"Feature","geometry":{"type":"Polygon","coordinates":["@@MK@CA@AAGDEB@NVFJG"],"encodeOffsets":[[116281,22734]]},"properties":{"cp":[113.549052,22.199175],"name":"花王堂区","childNum":1}},{"id":"820003","type":"Feature","geometry":{"type":"Polygon","coordinates":["@@EGOB@DNLHE@C"],"encodeOffsets":[[116285,22729]]},"properties":{"cp":[113.550252,22.193791],"name":"望德堂区","childNum":1}},{"id":"820004","type":"Feature","geometry":{"type":"Polygon","coordinates":["@@ŸYMVAN@BFCBBDAFHDBBFDHIJJEFDPCHHlYJQ"],"encodeOffsets":[[116313,22707]]},"properties":{"cp":[113.55374,22.188119],"name":"大堂区","childNum":1}},{"id":"820005","type":"Feature","geometry":{"type":"Polygon","coordinates":["@@JICGAECACGEBAAEDBFNXB@"],"encodeOffsets":[[116266,22728]]},"properties":{"cp":[113.54167,22.187778],"name":"风顺堂区","childNum":1}},{"id":"820006","type":"Feature","geometry":{"type":"Polygon","coordinates":["@@ ZNWRquZCBCC@AEA@@ADCDCAACEAGBQ@INEL"],"encodeOffsets":[[116265,22694]]},"properties":{"cp":[113.558783,22.154124],"name":"嘉模堂区","childNum":1}},{"id":"820007","type":"Feature","geometry":{"type":"Polygon","coordinates":["@@MOIAIEI@@GE@AAUCBdCFIFR@HAFBBDDBDCBC@@FB@BDDDA\\M"],"encodeOffsets":[[116316,22676]]},"properties":{"cp":[113.56925,22.136546],"name":"路凼填海区","childNum":1}},{"id":"820008","type":"Feature","geometry":{"type":"Polygon","coordinates":["@@DKMMa_GC_COD@dVDBBF@@HJ@JFJBNPZK"],"encodeOffsets":[[116329,22670]]},"properties":{"cp":[113.559954,22.124049],"name":"圣方济各堂区","childNum":1}}],"UTF8Encoding":true}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
src/assets/rmb.js Normal file
View File

@ -0,0 +1 @@
window._iconfont_svg_string_='<svg><symbol id="icon-renminbi" viewBox="0 0 1024 1024"><path d="M851.2 57.6 608 465.066667l211.2 0 0 72.533333L561.066667 537.6l0 115.2 260.266667 0L821.333333 725.333333 561.066667 725.333333l0 241.066667-96 0L465.066667 725.333333 206.933333 725.333333l0-72.533333 258.133333 0 0-115.2L206.933333 537.6l0-72.533333 211.2 0L172.8 57.6l106.666667 0 234.666667 401.066667L746.666667 57.6 851.2 57.6z" fill="#272636" ></path></symbol></svg>',function(n){var t=(t=document.getElementsByTagName("script"))[t.length-1],e=t.getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var i,o,d,c,l,s=function(t,e){e.parentNode.insertBefore(t,e)};if(e&&!n.__iconfont__svg__cssinject__){n.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(t){console&&console.log(t)}}i=function(){var t,e=document.createElement("div");e.innerHTML=n._iconfont_svg_string_,(e=e.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",e=e,(t=document.body).firstChild?s(e,t.firstChild):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(i,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),i()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(d=i,c=n.document,l=!1,r(),c.onreadystatechange=function(){"complete"==c.readyState&&(c.onreadystatechange=null,a())})}function a(){l||(l=!0,d())}function r(){try{c.documentElement.doScroll("left")}catch(t){return void setTimeout(r,50)}a()}}(window);

View File

@ -368,7 +368,6 @@ function InvoiceManagement(props: {}) {
}).catch(function (error) { }).catch(function (error) {
console.log(error) console.log(error)
}) })
} }
useEffect(() => { useEffect(() => {

View File

@ -1,6 +1,467 @@
import type {DatePickerProps, TimePickerProps} from 'antd';
import {theme, DatePicker, Select, Space, Card, Statistic} from 'antd';
import {ArrowDownOutlined, ArrowUpOutlined, createFromIconfontCN} from '@ant-design/icons'
import {useEffect, useRef, useState} from "react";
import ReactECharts from 'echarts-for-react';
import * as Echarts from 'echarts'
import china from '../../assets/mapjson/china.json'
// @ts-ignore
Echarts.registerMap('china', china);
const {Option} = Select;
type PickerType = 'month' | 'year';
const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_3830502_eyfw75skyu.js',
});
function HorizontalBarChart(props:{title:string, values: number[], labels: string[]}) {
return <ReactECharts option={{
color: [
'#6395f9',
'#d4e2fd',
'#73deb3',
'#d4f5e8',
'#657798',
'#d0d6e0',
'#7666f9',
'#d5d0fd',
'#f6c022',
'#fcecbd',
],
title: {
text: props.title,
left: 'center'
},
tooltip: {
trigger: 'item'
},
yAxis: {
axisLine: {
show: false
},
axisTick: {
alignWithLabel: true
},
type: 'category',
inverse: true,
data: props.labels,
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value'
},
series: [
{
name: 'Direct',
type: 'bar',
colorBy: 'data',
emphasis: {
focus: 'series'
},
data: props.values
},
]
}} notMerge={true} lazyUpdate={true}/>
}
function StatView() { function StatView() {
return( const {
<div></div> token: {colorBgContainer, colorPrimary, colorSuccess},
} = theme.useToken();
const [type, setType] = useState<PickerType>('month');
const [loading, setLoading] = useState(true)
const [amounts, setAmounts] = useState([0, 0])
const [invoiceKinds, setInvoiceKinds] = useState<any>([])
const [departmentPieData, setDepartmentPieData] = useState<any>([])
const [departureNames, setDepartureNames] = useState<any>([])
const [departureValues, setDepartureValues] = useState<any>([])
const [destinationNames, setDestinationNames] = useState<any>([])
const [destinationValues, setDestinationValues] = useState<any>([])
useEffect(() => {
setAmounts([120, 43])
setInvoiceKinds([{value: 1048, name: '增值税发票'},
{value: 735, name: '火车票'},
{value: 580, name: '机票行程单'},
{value: 484, name: '通用机打发票'},
{value: 300, name: '过路过桥费发票'}])
setDepartmentPieData([{value: 1048, name: '钝角部'},
{value: 735, name: '投影立体角部'},
{value: 580, name: '财务部'},
{value: 484, name: '采购部'},
{value: 300, name: '销售部'}])
setDepartureNames(['北京', '上海', '广州', '深圳', '成都'])
setDepartureValues([100,80,70,60,50])
setDestinationNames(['北京', '上海', '广州', '深圳', '成都'])
setDestinationValues([100,80,70,60,50])
setLoading(false)
}, []);
return (
<div>
<div style={{
height: 72,
padding: '30px',
backgroundColor: colorBgContainer,
display: "flex",
flexDirection: "row",
alignItems: "center"
}}>
<Space>
<Select value={type} onChange={setType}>
<Option value="month"></Option>
<Option value="year"></Option>
</Select>
<DatePicker picker={type} onChange={(value) => console.log(value)}/>
</Space>
</div>
{loading ?
<div></div> :
<div>
<div style={{
width: '100%',
padding: '30px 30px 0px 30px',
display: "flex",
flexDirection: "row",
flexWrap: "wrap"
}}>
<div style={{flex: 1, flexBasis: 400, display: "flex", flexDirection: "row", flexWrap: "wrap"}}>
<Card style={{flex: 1, flexBasis: 200, margin: 10}}>
<Statistic title="报销金额" value={1128.45} precision={2}
prefix={<IconFont type={'icon-renminbi'}/>}/>
</Card>
<Card style={{flex: 1, flexBasis: 200, margin: 10}}>
<Statistic
title={type === 'month' ? "较上个月" : '较去年'}
value={11.28}
precision={2}
valueStyle={{color: '#3f8600'}}
prefix={<ArrowUpOutlined/>}
suffix="%"
/>
</Card>
</div>
<div style={{flex: 1, flexBasis: 400, display: "flex", flexDirection: "row", flexWrap: "wrap"}}>
<Card style={{flex: 1, flexBasis: 200, margin: 10}}>
<Statistic title="补贴金额" value={1128.45} precision={2}
prefix={<IconFont type={'icon-renminbi'}/>}/>
</Card>
<Card style={{flex: 1, flexBasis: 200, margin: 10}}>
<Statistic
title={type === 'month' ? "较上个月" : '较去年'}
value={11.28}
precision={2}
valueStyle={{color: '#3f8600'}}
prefix={<ArrowUpOutlined/>}
suffix="%"
/>
</Card>
</div>
</div>
<div style={{
width: '100%',
padding: '0 30px 0 30px',
display: "flex",
flexDirection: "row",
flexWrap: "wrap"
}}>
<Card style={{flex: 1, flexBasis: 300, margin: 10, minWidth: 0, minHeight: 0}}>
<ReactECharts
option={{
color: [
'#6395f9',
'#62daab',
],
title: {
text: '发票与报销单新增情况',
left: 'center'
},
tooltip: {
trigger: 'item'
},
xAxis: {
axisLine: {
show: false
},
axisTick: {
alignWithLabel: true
},
type: 'category',
data: ['发票', '报销单'],
},
yAxis: {
type: 'value'
},
series: [
{
type: 'bar',
data: amounts,
colorBy: 'data',
barWidth: '80px',
itemStyle: {
borderRadius: [10, 20, 40, 40]
}
}
],
grid: {
bottom: '20px'
}
}}
notMerge={true}
lazyUpdate={true}
/>
</Card>
<Card style={{flex: 1, flexBasis: 300, margin: 10, minWidth: 0, minHeight: 0}}>
<ReactECharts
option={{
title: {
text: '发票类型',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
type: 'scroll',
bottom: '0px',
left: 'center'
},
series: [
{
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: invoiceKinds
}
]
}}
notMerge={true}
lazyUpdate={true}
/>
</Card>
<Card style={{flex: 1, flexBasis: 300, margin: 10, minWidth: 0, minHeight: 0}}>
<ReactECharts
option={{
title: {
text: '各部门报销金额',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
type: 'scroll',
bottom: '0px',
left: 'center'
},
series: [
{
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: departmentPieData
}
]
}}
notMerge={true}
lazyUpdate={true}
/>
</Card>
</div>
<div style={{
width: '100%',
padding: '0px 30px 0px 30px',
display: "flex",
flexDirection: "row",
flexWrap: "wrap"
}}>
<div style={{flex: 2, flexBasis: 600, display: "flex", flexDirection: "row", flexWrap: "wrap"}}>
<Card style={{flex: 1, flexBasis: 600, margin: 10, minWidth: 0, minHeight: 0}}>
<ReactECharts style={{height: 600}} option={{
title: {
text: '差旅流向图',
left: 'center',
},
series: [
{
type: 'map',
map: 'china',
emphasis: {
disabled: true
},
select: {
disabled: true
}
}
]
}} notMerge={true}
lazyUpdate={true}/>
</Card>
</div>
<div style={{flex: 1, flexBasis: 300, display: "flex", flexDirection: "row", flexWrap: "wrap"}}>
<Card style={{flex: 1, flexBasis: 300, margin: 10, minWidth: 0, minHeight: 0}}>
<HorizontalBarChart title={'出发地'} values={departureValues} labels={departureNames}/>
</Card>
<Card style={{flex: 1, flexBasis: 300, margin: 10, minWidth: 0, minHeight: 0}}>
<HorizontalBarChart title={'目的地'} values={destinationValues} labels={destinationNames}/>
</Card>
</div>
</div>
<Card style={{margin: '10px 40px 40px 40px', minWidth: 0, minHeight: 0}}>
<ReactECharts option={{
title: {
text: '各部门报销金额',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
legend: {
type: 'scroll',
data: ['Email', 'Union Ads', 'Video Ads', 'Direct', 'Search Engine'],
top: 40
},
grid: {
top: 80,
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: 'Email',
type: 'line',
stack: 'Total',
areaStyle: {},
emphasis: {
focus: 'series'
},
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: 'Union Ads',
type: 'line',
stack: 'Total',
areaStyle: {},
emphasis: {
focus: 'series'
},
data: [220, 182, 191, 234, 290, 330, 310]
},
{
name: 'Video Ads',
type: 'line',
stack: 'Total',
areaStyle: {},
emphasis: {
focus: 'series'
},
data: [150, 232, 201, 154, 190, 330, 410]
},
{
name: 'Direct',
type: 'line',
stack: 'Total',
areaStyle: {},
emphasis: {
focus: 'series'
},
data: [320, 332, 301, 334, 390, 330, 320]
},
{
name: 'Search Engine',
type: 'line',
stack: 'Total',
label: {
show: true,
position: 'top'
},
areaStyle: {},
emphasis: {
focus: 'series'
},
data: [820, 932, 901, 934, 1290, 1330, 1320]
}
]
}}
notMerge={true}
lazyUpdate={true}/>
</Card>
</div>
}
</div>
) )
} }
export default StatView export default StatView