Browse Source

feat(): 欠费总分析done

tags/PMS_Frontend_v1.0.6-develop
wanghx 1 year ago
parent
commit
37e633629d
  1. 32
      src/config/character.config.js
  2. 67
      src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/Overview/echarts.config.js
  3. 64
      src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/Overview/index.jsx
  4. 167
      src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/ParkArrear/index.jsx
  5. 40
      src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/ParkArrear/index.scss
  6. 295
      src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/PlateArrear/index.jsx
  7. 56
      src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/PlateArrear/index.scss
  8. 5
      src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/index.scss
  9. 73
      src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/loadable.jsx
  10. 28
      src/services/DataAnalysisPrediction/ParkingIncomeAly/index.js

32
src/config/character.config.js

@ -718,6 +718,38 @@ export default {
value: 4,
},
],
sectionType: [
{
label: "全部",
value: -1
},
{
label: "200以内",
value: 1
},
{
label: "200-500",
value: 2
},
{
label: "500以上",
value: 3
}
],
vipType: [
{
label: "全部",
value: -1
},
{
label: "非会员",
value: 0
},
{
label: "会员",
value: 1
},
]
}
//商户名称
export const merchantName = [

67
src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/Overview/echarts.config.js

@ -1,4 +1,4 @@
const colorList = ['#3AA9FF', '#F997DF']
const colorList = ['#3AA9FF', '#F997DF', '#F9EF97']
import utils from "@/config/utils"
export default {
@ -73,4 +73,69 @@ export default {
}
]
},
lineChartOption: {
title: {
text: ''
},
tooltip: {
trigger: 'axis'
},
legend: {
icon: 'rect',
top: '5%',
left: 'center',
itemHeight: 6,
itemGap: 20,
textStyle: {
color: '#fff'
}
},
grid: {
top: '15%',
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: [],
axisLabel: {
color: 'rgba(255, 255, 255, .65)'
}
},
yAxis: {
type: 'value',
axisLine: {
show: false
},
axisLabel: {
color: 'rgba(255, 255, 255, .65)'
},
splitLine: {
show: true,
lineStyle: {
type: 'dashed',
color: '#fff',
opacity: .15
}
}
},
series: [
{
name: '',
type: 'line',
stack: 'Total',
symbolSize: 1,
symbol: 'circle',
showSymbol: false,
smooth: true,
lineStyle: {
width: 3
},
data: []
}
]
}
}

64
src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/Overview/index.jsx

@ -1,4 +1,4 @@
import React, { useEffect, useState, useRef } from 'react'
import React, { useEffect, useState, useImperativeHandle, forwardRef } from 'react'
import ajax from "@/services"
import { Tabs } from 'antd'
import ReactEcharts from "echarts-for-react";
@ -6,7 +6,7 @@ import EchartsConfig from './echarts.config'
import utils from "@/config/utils"
import './index.scss'
const Overview = function(props) {
const Overview = forwardRef((props, ref) => {
const {
formData
} = props
@ -15,6 +15,7 @@ const Overview = function(props) {
const [activeTab, setActiveTab] = useState('parkRank')
const [moneyPieChartOption, setMoneyPieChartOption] = useState({...EchartsConfig.pieChartOption})
const [itemPieChartOption, setItemPieChartOption] = useState({...EchartsConfig.pieChartOption})
const [trendLineChartOption, setTrendLineChartOption] = useState({...EchartsConfig.lineChartOption})
const ajaxGetData = () => {
ajax.getIncomeOverviewData({...formData}).then(res => {
@ -92,6 +93,51 @@ const Overview = function(props) {
})
}
const initTrendLineChart = (data) => {
let [title, stack1, stack2, stack3] = [[], [], [], []]
data.map(item => {
title.push(item.name)
stack1.push(item.park1)
stack2.push(item.park2)
stack3.push(item.park3)
})
setTrendLineChartOption({
...trendLineChartOption,
xAxis: {
...trendLineChartOption.xAxis,
data: [...title]
},
series: [
{
...trendLineChartOption.series[0],
name: '一类区',
type: 'line',
stack: 'Total',
data: [...stack1]
},
{
...trendLineChartOption.series[0],
name: '二类区',
type: 'line',
stack: 'Total',
data: [...stack2]
},
{
...trendLineChartOption.series[0],
name: '三类区',
type: 'line',
stack: 'Total',
data: [...stack3]
}
]
})
}
useImperativeHandle(ref, () => ({
ajaxGetData
}))
useEffect(() => {
ajaxGetData()
}, [])
@ -103,6 +149,9 @@ const Overview = function(props) {
if (yisaData.items) {
initItemPieChart(yisaData.items, '个')
}
if (yisaData.trend) {
initTrendLineChart(yisaData.trend)
}
}, [yisaData])
return (
@ -160,12 +209,17 @@ const Overview = function(props) {
</div>
<div className="overview-bottom">
<div className="overview-item">
<div className="overview-item-title">欠费金额趋势</div>
<div className="overview-item-content" id="trendChart"></div>
<div className="overview-item-title">欠费金额趋势图</div>
<div className="overview-item-content">
<ReactEcharts
option={trendLineChartOption}
style={{ overflow: "hidden" }}
/>
</div>
</div>
</div>
</div>
)
}
})
export default Overview

167
src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/ParkArrear/index.jsx

@ -1,11 +1,170 @@
import React from 'react'
import React, { useEffect, useState, useImperativeHandle, forwardRef } from 'react'
import { Table, Pagination, Button } from 'antd'
import { ResultFlowResult, ExportBtnNew } from '@/components'
import { dictionary } from "@/config/common"
import ajax from "@/services"
import './index.scss'
const ParkArrear = forwardRef((props, ref) => {
const {
formData
} = props
const [tableLoading, setTableLoading] = useState(false)
const [pageInfo, setPageInfo] = useState({
pn: 1,
length: 10
})
const [exportData, setExportData] = useState({
start: 1,
end: 10
})
const [resultData, setResultData] = useState({
list: [],
totalRecords: 0
})
const tableColumns = [
{
title: "序号",
width: 60,
align: 'center',
render: (text, record, index) => index + 1,
},
{
title: "停车场名称",
align: 'center',
dataIndex: "name",
},
{
title: "区域",
align: 'center',
dataIndex: "area",
},
{
title: "累计营收金额",
align: 'center',
dataIndex: "ljys",
},
{
title: "累计实收金额",
align: 'center',
dataIndex: "ljss",
},
{
title: "现存欠费金额",
align: 'center',
dataIndex: "xcqfje",
},
{
title: "现存欠费订单",
align: 'center',
dataIndex: 'xcqfdd'
},
{
title: '欠费金额占比',
align: 'center',
dataIndex: 'ratio',
render: (text, record) => {
return text + '%'
}
}
]
const paginationProps = {
className: "pagination-common",
showQuickJumper: true,
showSizeChanger: true,
current: pageInfo.pn,
total: resultData?.totalRecords,
pageSize: pageInfo.length,
pageSizeOptions: Array.from(
new Set([...[15], ...(dictionary?.pageSizeOptions || [])])
),
onChange: (current, size) => {
setPageInfo({
...pageInfo,
...{ pn: current, length: size }
});
}
}
const ajaxGetTableData = (data) => {
setTableLoading(true)
ajax.getIncomeParkArrearData({
...data
}).then(res => {
if (res.status == 20000) {
setTableLoading(false)
setResultData({
list: res.data,
totalRecords: res.totalRecords
})
setExportData({
...exportData,
start: 1,
end: res.totalRecords
})
}
})
}
const getData = (newPageInfo) => {
setPageInfo({
...newPageInfo
})
ajaxGetTableData({
...formData,
...newPageInfo
})
}
useImperativeHandle(ref, () => ({
getData
}))
useEffect(() => {
ajaxGetTableData({
...formData,
...pageInfo
})
}, [JSON.stringify(pageInfo)])
const ParkArrear = function(props) {
return (
<div className="park-arrear-container">
ParkArrear
<div className="park-table-title">
<div className="park-table-info">共查询到<span>{ resultData.totalRecords || 0 }</span>条结果</div>
<div className="park-table-export">
<ExportBtnNew
children={<Button className="export-btn" size='medium' type="primary">导出</Button>}
modalType="noImg"
totalRecords={resultData.totalRecords}
exportUrl="/api/dataAnalysis/arrearsPark/export"
postdata={{
formData: {...formData, ...pageInfo}
}}
imgno={false}
/>
</div>
</div>
<div className="park-table-content">
<ResultFlowResult
ajaxLoad={tableLoading}
resultData={resultData?.list || []}
>
<Table
className='park-table'
// rowKey={(row) => row.id}
dataSource={resultData?.list || []}
columns={tableColumns}
pagination={false}
scroll={{y: 585}}
loading={tableLoading}
/>
<Pagination {...paginationProps} className="pagination-common" />
</ResultFlowResult>
</div>
</div>
)
}
})
export default ParkArrear

40
src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/ParkArrear/index.scss

@ -0,0 +1,40 @@
@import "@/assets/css/mixin.scss";
.park-table-title {
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
.park-table-info {
span {
color: var(--color-primary);
}
}
.park-table-export {
}
}
.park-arrear-container {
height: 100%;
display: flex;
flex-direction: column;
.park-table-title {
height: 25px;
}
.park-table-content {
flex: 1;
.park-table {
flex: 1;
.ant-table-body {
@include scrollBar(var(--color-user-list-bg), #3B97FF);
}
.ant-table-fixed-header .ant-table-tbody tr:nth-child(2n+1) > td {
background: unset !important;
background-color: #3e4557 !important;
}
.ant-table-fixed-header .ant-table-tbody tr:nth-child(2n+1) > td {
}
}
}
}

295
src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/PlateArrear/index.jsx

@ -1,10 +1,299 @@
import React from 'react'
import React, {useEffect, useState, useImperativeHandle, forwardRef} from 'react'
import { ResultFlowResult, ExportBtnNew } from '@/components'
import { Table, Pagination, Button, Modal } from 'antd'
import { dictionary } from "@/config/common"
import ajax from "@/services"
import './index.scss'
const PlateArrear = function(props) {
const PlateArrear = forwardRef((props, ref) => {
const {
formData
} = props
const [tableLoading, setTableLoading] = useState(false)
const [detailVisble, setDetailVisible] = useState(false)
const [detailPlate, setDetailPlate] = useState('')
const [pageInfo, setPageInfo] = useState({
pn: 1,
length: 10
})
const [exportData, setExportData] = useState({
start: 1,
end: 10
})
const [resultData, setResultData] = useState({
list: [],
totalRecords: 0
})
const handleDetail = (data) => {
setDetailPlate(data.plate)
setDetailVisible(true)
}
const tableColumns = [
{
title: "序号",
width: 60,
align: 'center',
render: (text, record, index) => index + 1,
},
{
title: "车牌号",
align: 'center',
dataIndex: "plate",
},
{
title: "手机号",
align: 'center',
dataIndex: "phone",
},
{
title: "欠费金额",
align: 'center',
dataIndex: "money",
},
{
title: '操作',
width: 200,
align: 'center',
render: (text, record) => {
return (
<Button type='primary' onClick={() => handleDetail(record)}>详情</Button>
)
}
}
]
const paginationProps = {
className: "pagination-common",
showQuickJumper: true,
showSizeChanger: true,
current: pageInfo.pn,
total: resultData?.totalRecords,
pageSize: pageInfo.length,
pageSizeOptions: Array.from(
new Set([...[15], ...(dictionary?.pageSizeOptions || [])])
),
onChange: (current, size) => {
setPageInfo({
...pageInfo,
...{ pn: current, length: size }
});
}
}
const ajaxGetTableData = (data) => {
setTableLoading(true)
ajax.getIncomePlateArrearData({
...data
}).then(res => {
if (res.status == 20000) {
setTableLoading(false)
setResultData({
list: res.data,
totalRecords: res.totalRecords
})
setExportData({
...exportData,
start: 1,
end: res.totalRecords
})
}
})
}
const getData = (newPageInfo) => {
setPageInfo({
...newPageInfo
})
ajaxGetTableData({
...formData,
...newPageInfo
})
}
useImperativeHandle(ref, () => ({
getData
}))
useEffect(() => {
ajaxGetTableData({
...formData,
...pageInfo
})
}, [JSON.stringify(pageInfo)])
return (
<div className="plate-arrear-container">
PlateArrear
<div className="park-table-title">
<div className="park-table-info">共查询到<span>{ resultData.totalRecords || 0 }</span>条结果</div>
<div className="park-table-export">
<ExportBtnNew
children={<Button className="export-btn" size='medium' type="primary">导出</Button>}
modalType="noImg"
totalRecords={resultData.totalRecords}
exportUrl="/api/dataAnalysis/arrearsPlate/export"
postdata={{
formData: {...formData, ...pageInfo}
}}
imgno={false}
/>
</div>
</div>
<div className="park-table-content">
<ResultFlowResult
ajaxLoad={tableLoading}
resultData={resultData?.list || []}
>
<Table
className='park-table'
// rowKey={(row) => row.id}
dataSource={resultData?.list || []}
columns={tableColumns}
pagination={false}
scroll={{y: 585}}
loading={tableLoading}
/>
<Pagination {...paginationProps} className="pagination-common" />
</ResultFlowResult>
</div>
<ModalDetail
visible={detailVisble}
plate_number={detailPlate}
onOk={() => setDetailVisible(false)}
onCancel={() => setDetailVisible(false)}
/>
</div>
)
})
const ModalDetail = (props) => {
const {
visible,
onOk,
plate_number,
onCancel
} = props
const [tableLoading, setTableLoading] = useState(false)
const [resultData, setResultData] = useState({
list: [],
totalRecords: 0
})
const [formData, setFormData] = useState({
pn: 1,
length: 10,
plate_number: plate_number
})
const [pageInfo, setPageInfo] = useState({
pn: 1,
length: 10
})
const tableColumns = [
{
title: "车牌号",
align: 'center',
dataIndex: "plate",
},
{
title: "停车场",
align: 'center',
dataIndex: "park",
},
{
title: "入场时间",
align: 'center',
dataIndex: "entry_time",
},
{
title: "出场时间",
align: 'center',
dataIndex: "leave_time",
},
{
title: "停车时长",
align: 'center',
dataIndex: "duration",
},
{
title: "欠费金额",
align: 'center',
dataIndex: "money",
},
]
const paginationProps = {
className: "pagination-common",
showQuickJumper: true,
showSizeChanger: true,
current: pageInfo.pn,
total: resultData?.totalRecords,
pageSize: pageInfo.length,
pageSizeOptions: Array.from(
new Set([...[15], ...(dictionary?.pageSizeOptions || [])])
),
onChange: (current, size) => {
setPageInfo({
...pageInfo,
...{ pn: current, length: size }
});
}
}
const ajaxGetDetailData = () => {
setTableLoading(true)
ajax.getPlateArrearData({...formData, ...pageInfo}).then(res => {
if (res.status == 20000) {
setTableLoading(false)
setResultData({
list: res.data,
totalRecords: res.totalRecords
})
}
})
}
useEffect(() => {
setFormData({
pn: 1,
length: 10,
plate_number: plate_number
})
}, [plate_number])
useEffect(() => {
ajaxGetDetailData()
}, [JSON.stringify(formData), JSON.stringify(pageInfo)])
return (
<Modal
className="yisa-modal detail-modal"
title={"弹窗详情"}
open={visible}
width={700}
onCancel={onCancel}
onOk={onOk}
>
<div className="detail-modal-container">
<div className="detail-title">欠费总分析详情</div>
<ResultFlowResult
ajaxLoad={tableLoading}
resultData={resultData?.list || []}
>
<Table
className='park-table'
// rowKey={(row) => row.id}
dataSource={resultData?.list || []}
columns={tableColumns}
pagination={false}
scroll={{y: 200}}
loading={tableLoading}
/>
<Pagination {...paginationProps} className="pagination-common" />
</ResultFlowResult>
</div>
</Modal>
)
}

56
src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/PlateArrear/index.scss

@ -0,0 +1,56 @@
@import "@/assets/css/mixin.scss";
.park-table-title {
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
.park-table-info {
span {
color: var(--color-primary);
}
}
.park-table-export {
}
}
.plate-arrear-container {
height: 100%;
display: flex;
flex-direction: column;
.park-table-title {
height: 25px;
}
.park-table-content {
flex: 1;
.park-table {
flex: 1;
.ant-table-body {
@include scrollBar(var(--color-user-list-bg), #3B97FF);
}
.ant-table-fixed-header .ant-table-tbody tr:nth-child(2n+1) > td {
background: unset !important;
background-color: #3e4557 !important;
}
.ant-table-fixed-header .ant-table-tbody tr:nth-child(2n+1) > td {
}
}
}
}
.detail-modal {
.ant-modal-body {
padding-top: 0;
padding-bottom: 5px;
.detail-title {
margin-bottom: 10px;
}
.ant-table-thead>tr>th {
background-color: #616b83;
}
.ant-table-body {
@include scrollBar(var(--color-user-list-bg), #3B97FF);
}
}
}

5
src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/index.scss

@ -86,6 +86,11 @@ $color-primary : var(--color-primary);
background: var(--button-default-bg);
}
.btn-export {
width: 90px;
height: 36px;
}
.submit {
width: calc(100% - 100px);
height: 36px;

73
src/pages/DataAnalysisPrediction/ParkingIncomeAly/ArrearageAly/loadable.jsx

@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useRef } from "react";
import { ResultFlowResult } from "@/components";
import { Select, Input, Button, Table, message, Pagination, DatePicker, Modal, Cascader, Tooltip, Tabs } from "antd";
import { Select, Input, Button, Table, message, Pagination, DatePicker, Cascader, Tooltip, Tabs } from "antd";
import { useSessionStorageState } from "ahooks";
import { dictionary } from "@/config/common";
import { useNavigate } from "react-router-dom";
@ -22,10 +22,19 @@ function ArrearageAly(props) {
park_text: "",
pay_merchant_id: 0
}
const defaultNewData = {
section: -1,
plate_number: "",
is_vip: -1
}
const [formData, setFormData] = useState({...defaultData})
const [formDataNew, setFormDataNew] = useState({...defaultNewData})
const [areaList, setAreaList] = useState([])
const [loading, setLoading] = useState(false)
const [activeTab, setActiveTab] = useState(1)
const overviewRef = useRef(null)
const parkRef = useRef(null)
const plateRef = useRef(null)
const tabItems = [
{
@ -54,7 +63,19 @@ function ArrearageAly(props) {
}
const handleSearch = () => {
if (activeTab == 1) {
overviewRef.current.ajaxGetData()
} else if (activeTab == 2) {
parkRef.current.getData({
pn: 1,
length: 10
})
} else if (activeTab == 3) {
plateRef.current.getData({
pn: 1,
length: 10
})
}
}
useEffect(() => {
@ -66,6 +87,43 @@ function ArrearageAly(props) {
<div className="parking-search">
<div className="title">查询条件</div>
<div className="form-Wrap">
{
activeTab == 3 ? (
<>
<div className="yisa-search">
<label>收费区间</label>
<Select
className="form-con"
value={formDataNew.section}
options={dictionary.sectionType}
onChange={(v) => setFormDataNew({...formDataNew, section: v})}
placeholder="请选择收费区间"
/>
</div>
<div className="yisa-search">
<label>车牌号</label>
<Input
className="form-con"
placeholder="请输入车牌号"
value={formDataNew?.plate_number}
onChange={(e) =>
setFormDataNew({ ...formDataNew, plate_number: e.target.value })
}
/>
</div>
<div className="yisa-search">
<label>是否会员</label>
<Select
className="form-con"
value={formDataNew.is_vip}
options={dictionary.vipType}
onChange={(v) => setFormDataNew({...formDataNew, is_vip: v})}
placeholder="请选择是否会员"
/>
</div>
</>
) : (
<>
<div className="yisa-search">
<label>区域</label>
<Cascader
@ -106,6 +164,9 @@ function ArrearageAly(props) {
}
/>
</div>
</>
)
}
<div className="form-btn">
<Button
className="reset"
@ -132,13 +193,13 @@ function ArrearageAly(props) {
</div>
<div className="parking-wrapper">
{
activeTab == 1 ? <Overview formData={formData}/> : null
activeTab == 1 ? <Overview ref={overviewRef} formData={formData}/> : null
}
{
activeTab == 2 ? <ParkArrear formData={formData}/> : null
activeTab == 2 ? <ParkArrear ref={parkRef} formData={formData}/> : null
}
{
activeTab == 3 ? <PlateArrear formData={formData}/> : null
activeTab == 3 ? <PlateArrear ref={plateRef} formData={formDataNew}/> : null
}
</div>
</div>

28
src/services/DataAnalysisPrediction/ParkingIncomeAly/index.js

@ -7,5 +7,33 @@ export default {
type: 'post',
data
})
},
getIncomeParkArrearData: function(data) {
return ajax({
url: '/api/dataAnalysis/arrearsPark',
type: 'post',
data
})
},
parkArrearExport: function(data) {
return ajax({
url: '/api/dataAnalysis/arrearsPark/export',
type: 'post',
data
})
},
getIncomePlateArrearData: function(data) {
return ajax({
url: '/api/dataAnalysis/arrearsPlate',
type: 'post',
data
})
},
getPlateArrearData: function(data) {
return ajax({
url: '/api/dataAnalysis/arrearsPlate/details',
type: 'post',
data
})
}
}
Loading…
Cancel
Save