737 lines
30 KiB
737 lines
30 KiB
import React, { useState, useEffect } from "react";
|
|
import { ResultFlowResult } from "@/components";
|
|
import { Select, Input, Button, Table, message, Pagination, DatePicker, Modal, Cascader, Tooltip } from "antd";
|
|
import { Icon } from "@/components"
|
|
import { useSessionStorageState } from "ahooks";
|
|
import { lineChartOption, ringChartOption } from "../echarts.config";
|
|
import moment from "moment";
|
|
import ReactEcharts from "echarts-for-react";
|
|
import "./index.scss";
|
|
import ajax from "@/services";
|
|
//停车时段分析
|
|
function ParkingAlyPeriod() {
|
|
// session缓存
|
|
const [defaultParams, setDefaultParams] = useSessionStorageState(
|
|
"formData_parkingAlyPeriod",
|
|
{ defaultValue: null }
|
|
);
|
|
//区域的下拉数据
|
|
const [areaList, setAreaList] = useState([]);
|
|
// 默认数据
|
|
const defaultData = {
|
|
start_time: moment().subtract('days').startOf('day').format("YYYY-MM-DD"),
|
|
end_time: moment().endOf("day").format("YYYY-MM-DD"),
|
|
date_type: '1',
|
|
};
|
|
// 分页数据
|
|
const [pageInfo, setPageInfo] = useState({
|
|
pn: defaultParams ? defaultParams?.pn : 1,
|
|
page_size: defaultParams ? defaultParams?.page_size : 15,
|
|
});
|
|
// 表单数据
|
|
const [formData, setFormData] = useState({
|
|
...defaultData,
|
|
...defaultParams,
|
|
});
|
|
// 搜索提交数据-存储
|
|
const [holdData, setHoldData] = useState(formData);
|
|
// 访问接口,isAjax改变时执行
|
|
const [isAjax, setIsAjax] = useState(false);
|
|
// 检索按钮加载状态
|
|
const [loading, setLoading] = useState(false);
|
|
// 表格加载状态
|
|
const [tabLoading, setTabLoading] = useState(false);
|
|
// 表格返回数据
|
|
const [resultData, setResultData] = useState({
|
|
total: 0,
|
|
list: [],
|
|
});
|
|
//出入场车流量分析
|
|
const [revenueData, setRevenueData] = useState({});
|
|
//停车饱和度趋势分析
|
|
const [parkData, setParkData] = useState({});
|
|
const [searchSelectList, setSearchSelectList] = useState([]); //搜索下拉数据
|
|
const [sessionTabList, setSessionTabList] = useSessionStorageState('parkingAlyPeriod', {
|
|
value: {
|
|
}
|
|
})
|
|
useEffect(() => {
|
|
if (sessionTabList && Object.values(sessionTabList).length > 0) {
|
|
setFormData({
|
|
...formData, ...sessionTabList
|
|
})
|
|
getCheck({
|
|
...sessionTabList
|
|
})
|
|
} else {
|
|
getCheck()
|
|
}
|
|
}, [isAjax])
|
|
useEffect(() => {
|
|
setSessionTabList({
|
|
...formData
|
|
})
|
|
}, [formData])
|
|
useEffect(() => {
|
|
getSelectList();
|
|
}, []);
|
|
|
|
// 访问接口,获取表格
|
|
// useEffect(() => {
|
|
// getData();
|
|
// }, [isAjax]);
|
|
//时间状态切换
|
|
const TimeChange = () => {
|
|
let e = formData.date_type;
|
|
let str = "day";
|
|
let mat = "YYYY-MM-DD";
|
|
if (e == 4) {
|
|
str = "year";
|
|
mat = "YYYY";
|
|
} else if (e == 3) {
|
|
str = "month";
|
|
mat = "YYYY-MM";
|
|
} else if (e == 2) {
|
|
str = "week";
|
|
mat = "YYYY-MM-DD";
|
|
}
|
|
return { str, mat };
|
|
};
|
|
//切换时间变化
|
|
const SetTimeNow = (e) => {
|
|
let start = "";
|
|
let end = "";
|
|
if (e == 4) {
|
|
start = moment().format("YYYY");
|
|
end = moment().format("YYYY");
|
|
} else if (e == 3) {
|
|
start = moment().startOf('month').format("YYYY-MM-DD");
|
|
end = moment().endOf("month").format("YYYY-MM-DD");
|
|
} else if (e == 2) {
|
|
start = moment().day(1).format("YYYY-MM-DD");
|
|
end = moment().day(7).format("YYYY-MM-DD");
|
|
} else {
|
|
start = moment().startOf("day").format("YYYY-MM-DD");
|
|
end = moment().endOf("day").format("YYYY-MM-DD");
|
|
}
|
|
setFormData({
|
|
...formData,
|
|
date_type: e,
|
|
start_time: start,
|
|
end_time: end,
|
|
});
|
|
};
|
|
//出入场车流量分析 折线图
|
|
const getRevenueOption = (data) => {
|
|
const areaNames = data[0].name ? [...new Set(data.map((item) => item.name))] : ['入场车次', '出场车次'];
|
|
// 获取所有横坐标
|
|
const dates = [...new Set(data.map((item) => item.hour))].sort(
|
|
(a, b) => a.hour - b.hour
|
|
);
|
|
// 构建数据对象
|
|
const seriesData = areaNames.map((areaName, index) => {
|
|
// 获取数据
|
|
const areaData = data[0].name ? data.filter((item) => item.name === areaName) : data
|
|
// 构建数据对象
|
|
return {
|
|
name: areaNames.length > 1 ? areaName : '',
|
|
type: "line",
|
|
itemStyle: {
|
|
label: {
|
|
show: true, //开启显示
|
|
position: 'top', //在上方显示
|
|
color: 'white',//字体颜色
|
|
fontSize: 10//字体大小
|
|
},
|
|
},
|
|
data: dates.map((item) => {
|
|
for (const { hour, total_in_records, total_out_records } of areaData) {
|
|
if (hour === item) return index == 1 ? total_in_records : total_out_records;
|
|
}
|
|
return 0;
|
|
}),
|
|
};
|
|
});
|
|
// 构建X轴数据
|
|
const xAxisData = dates.map((date) => {
|
|
return {
|
|
value: date,
|
|
align: "center",
|
|
lineStyle: {
|
|
color: "skyblue", // 设置线的颜色为天蓝色
|
|
shadowBlur: 6,
|
|
},
|
|
|
|
};
|
|
});
|
|
setRevenueData(lineChartOption(areaNames, xAxisData, "车次数(个)", seriesData));
|
|
};
|
|
//停车饱和趋势分析分析 折线图
|
|
const getParkOption = (data) => {
|
|
// data = [
|
|
// {
|
|
// "hour": 0,
|
|
// "occupancy_rate": "0.01%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 1,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 2,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 3,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 4,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 5,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 6,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 7,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 8,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 9,
|
|
// "occupancy_rate": "0.1%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 10,
|
|
// "occupancy_rate": "0.01%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 11,
|
|
// "occupancy_rate": "0.04%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 12,
|
|
// "occupancy_rate": "0.06%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 13,
|
|
// "occupancy_rate": "0.04%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 14,
|
|
// "occupancy_rate": "0.06%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 15,
|
|
// "occupancy_rate": "0.01%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 16,
|
|
// "occupancy_rate": "0.04%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 17,
|
|
// "occupancy_rate": "0.04%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 18,
|
|
// "occupancy_rate": "0.07%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 19,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 20,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 21,
|
|
// "occupancy_rate": "0.01%",
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 22,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// },
|
|
// {
|
|
// "hour": 23,
|
|
// "occupancy_rate": 0,
|
|
// "charge_type": "2",
|
|
// "charge_type_name": "二类区"
|
|
// }
|
|
// ]
|
|
const areaNames = data[0].charge_type_name ? [...new Set(data.map((item) => item.charge_type_name))] : [''];
|
|
// 获取所有横坐标
|
|
const dates = [...new Set(data.map((item) => item.hour))].sort(
|
|
(a, b) => a.hour - b.hour
|
|
);
|
|
// 构建数据对象
|
|
const seriesData = areaNames.map((areaName, index) => {
|
|
// 获取数据
|
|
const areaData = data[0].charge_type_name ? data.filter((item) => item.charge_type_name === areaName) : data
|
|
// 构建数据对象
|
|
return {
|
|
name: areaName,
|
|
type: "line",
|
|
itemStyle: {
|
|
label: {
|
|
show: true, //开启显示
|
|
position: 'top', //在上方显示
|
|
color: 'white',//字体颜色
|
|
fontSize: 10//字体大小
|
|
},
|
|
},
|
|
data: dates.map((item) => {
|
|
for (const { hour, occupancy_rate } of areaData) {
|
|
if (hour === item) return parseFloat(occupancy_rate);
|
|
}
|
|
return 0;
|
|
}),
|
|
};
|
|
});
|
|
// 构建X轴数据
|
|
const xAxisData = dates.map((date) => {
|
|
return {
|
|
value: date,
|
|
align: "center",
|
|
lineStyle: {
|
|
color: "skyblue", // 设置线的颜色为天蓝色
|
|
shadowBlur: 6,
|
|
},
|
|
|
|
};
|
|
});
|
|
|
|
setParkData(lineChartOption(areaNames, xAxisData, "饱和度", seriesData));
|
|
};
|
|
|
|
function getParkingIncome(data) {
|
|
ajax
|
|
.getParkingAlyPeriodLine(data)
|
|
.then((res) => {
|
|
if (res.status === 20000) {
|
|
getRevenueOption(res.data.list);
|
|
setResultData(res.data)
|
|
}
|
|
})
|
|
.catch((err) => console.error(err));
|
|
}
|
|
function getParkingData(data) {
|
|
ajax
|
|
.getParkingAlyPeriodParkLine(data)
|
|
.then((res) => {
|
|
if (res.status === 20000) {
|
|
getParkOption(res.data.list);
|
|
}
|
|
})
|
|
.catch((err) => console.error(err));
|
|
}
|
|
// 获取下拉数据
|
|
const getSelectList = () => {
|
|
ajax.getOperator().then((e) => {
|
|
setSearchSelectList([
|
|
...searchSelectList,
|
|
...e.data
|
|
])
|
|
})
|
|
};
|
|
|
|
|
|
// 携带参数处理
|
|
const getCheck = (v) => {
|
|
let postData = { ...formData };
|
|
if (!loading) {
|
|
postData = { ...holdData };
|
|
}
|
|
setDefaultParams({ ...postData, ...pageInfo });
|
|
if (moment(formData.end_time) - moment(formData.start_time) > 1000 * 31 * 24 * 3600) {
|
|
message.error("时间范围限制为31天!")
|
|
setLoading(false);
|
|
setTabLoading(false);
|
|
return
|
|
}
|
|
setLoading(false);
|
|
setTabLoading(false);
|
|
let params = { ...postData, ...v, ...pageInfo }
|
|
//请求接口
|
|
getParkingIncome(params)
|
|
getParkingData(params)
|
|
};
|
|
// 检索数据
|
|
const handleSearch = () => {
|
|
setLoading(true);
|
|
setPageInfo({ ...pageInfo, ...{ pn: 1 } });
|
|
setHoldData(formData);
|
|
setIsAjax(!isAjax);
|
|
};
|
|
|
|
// 导出
|
|
const handleExport = () => {
|
|
if (tableData.list.area_list?.length > 0) {
|
|
let { pn, page_size, ...params } = defaultParams;
|
|
ajax.getParkingAlyDurationParkingExp(defaultParams).then(
|
|
(res) => {
|
|
if (res) {
|
|
window.open(res.data.export_url)
|
|
} else {
|
|
message.error(res?.message);
|
|
}
|
|
},
|
|
(err) => {
|
|
console.log(err);
|
|
}
|
|
);
|
|
} else {
|
|
message.error("暂无数据");
|
|
}
|
|
};
|
|
// useEffect(() => {
|
|
// getParkingIncome();
|
|
// }, []);
|
|
//区域下拉框数据
|
|
useEffect(() => {
|
|
ajax
|
|
.getAreaTree()
|
|
.then((res) => {
|
|
if (res.status === 20000) {
|
|
setAreaList(res.data);
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
console.error(err);
|
|
});
|
|
}, []);
|
|
return (
|
|
<>
|
|
<div className="edit-order-inquiry">
|
|
<div className="paid-search">
|
|
<div className="title">查询条件</div>
|
|
<div className="form-Wrap">
|
|
<div className="yisa-search">
|
|
<label>区域</label>
|
|
<Cascader
|
|
className="form-con"
|
|
popupClassName="start-exception-deal-cascader"
|
|
options={areaList}
|
|
placeholder="请选择区域"
|
|
expandTrigger="hover"
|
|
fieldNames={{
|
|
label: "name",
|
|
value: "id",
|
|
children: "children",
|
|
}}
|
|
value={formData.area_id}
|
|
onChange={(v, option) => {
|
|
setFormData({ ...formData, area_id: v ? v : null });
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="yisa-search">
|
|
<label>运营商</label>
|
|
<Select
|
|
className="form-con"
|
|
placeholder="请选择"
|
|
options={searchSelectList || []}
|
|
value={formData.operator_id}
|
|
onChange={(v) =>
|
|
setFormData({ ...formData, operator_id: v })
|
|
}
|
|
/>
|
|
</div>
|
|
<div className="yisa-search">
|
|
<label>车场类型</label>
|
|
<Select
|
|
className="form-con"
|
|
placeholder="请选择车场类型"
|
|
options={[
|
|
{
|
|
label: '全部',
|
|
value: '0',
|
|
},
|
|
{
|
|
label: '路内车场',
|
|
value: '1',
|
|
},
|
|
{
|
|
label: '路外车场',
|
|
value: '2',
|
|
},
|
|
]}
|
|
value={formData.car_parking_type}
|
|
onChange={(v) =>
|
|
setFormData({ ...formData, car_parking_type: v })
|
|
}
|
|
/>
|
|
</div>
|
|
<div className="yisa-search">
|
|
<label>停车场</label>
|
|
<Input
|
|
className="form-con"
|
|
placeholder="请输入"
|
|
value={formData?.road_name}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, road_name: e.target.value })
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
<div className="yisa-search">
|
|
<label>日期
|
|
<div className="daf">
|
|
<Select
|
|
value={formData.date_type}
|
|
// style={{
|
|
// width: "100%",
|
|
// }}
|
|
placeholder="请选择"
|
|
options={[
|
|
{
|
|
value: "1",
|
|
label: "日",
|
|
},
|
|
{
|
|
value: "2",
|
|
label: "周",
|
|
},
|
|
{
|
|
value: "3",
|
|
label: "月",
|
|
},
|
|
]}
|
|
onChange={(e) => SetTimeNow(e)}
|
|
/>
|
|
</div>
|
|
</label>
|
|
<DatePicker
|
|
style={{ width: "100%" }}
|
|
// showTime
|
|
format={TimeChange().mat}
|
|
picker={TimeChange().str}
|
|
allowClear={false}
|
|
value={formData.start_time ? moment(formData.start_time) : null}
|
|
onChange={(date, dateString) => {
|
|
if (TimeChange().str == "week") {
|
|
setFormData({
|
|
...formData,
|
|
start_time: date
|
|
? moment(date).day(1).format("YYYY-MM-DD")
|
|
: null,
|
|
});
|
|
} else if (TimeChange().str == "day") {
|
|
if (date > moment(formData.end_time)) {
|
|
setFormData({
|
|
...formData,
|
|
end_time: dateString,
|
|
start_time: formData.end_time,
|
|
});
|
|
} else {
|
|
setFormData({
|
|
...formData,
|
|
start_time: dateString,
|
|
});
|
|
}
|
|
} else if (TimeChange().str == "month") {
|
|
setFormData({ ...formData, start_time: moment(date).format("YYYY-MM-DD"), end_time: moment(date).endOf("month").format("YYYY-MM-DD") });
|
|
} else {
|
|
setFormData({ ...formData, start_time: dateString });
|
|
}
|
|
}}
|
|
disabledDate={(current) => current > moment(formData.end_time)}
|
|
/>
|
|
|
|
</div>
|
|
<div className="yisa-search">
|
|
<label>至</label>
|
|
<DatePicker
|
|
style={{ width: "100%" }}
|
|
// showTime
|
|
format={TimeChange().mat}
|
|
picker={TimeChange().str}
|
|
allowClear={false}
|
|
value={formData.end_time ? moment(formData.end_time) : null}
|
|
onChange={(date, dateString) => {
|
|
if (TimeChange().str == "week") {
|
|
setFormData({
|
|
...formData,
|
|
end_time: date
|
|
? moment(date).day(7).format("YYYY-MM-DD")
|
|
: null,
|
|
});
|
|
} else if (TimeChange().str == "day") {
|
|
if (date < moment(formData.start_time)) {
|
|
setFormData({
|
|
...formData,
|
|
start_time: dateString,
|
|
end_time: formData.start_time,
|
|
});
|
|
} else {
|
|
setFormData({
|
|
...formData,
|
|
end_time: dateString,
|
|
});
|
|
}
|
|
} else if (TimeChange().str == "month") {
|
|
setFormData({ ...formData, start_time: moment(date).startOf('month').format("YYYY-MM-DD"), end_time: moment(date).format("YYYY-MM-DD") });
|
|
} else {
|
|
setFormData({ ...formData, end_time: dateString });
|
|
}
|
|
}}
|
|
disabledDate={(current) =>
|
|
current < moment(formData.start_time)
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-btn">
|
|
<Button
|
|
className="reset"
|
|
onClick={() => setFormData(defaultData)}
|
|
>
|
|
重置
|
|
</Button>
|
|
<Button
|
|
className="submit"
|
|
type="primary"
|
|
onClick={handleSearch}
|
|
loading={loading}
|
|
>
|
|
查询
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="paid-result period-result">
|
|
<div className="result">
|
|
<div className="result-hd">
|
|
<div className="result-header rea">
|
|
<div className="result-icon"><Icon type="shijian" /></div>
|
|
<div className="result-content">
|
|
<div className="title">停车高峰时段</div>
|
|
<div className="time">{resultData.peak_hours || "--"}</div>
|
|
</div>
|
|
</div>
|
|
<div className="result-header reb">
|
|
<div className="result-icon"><Icon type="shijian" /></div>
|
|
<div className="result-content">
|
|
<div className="title">入场压力时段</div>
|
|
<div className="time">{resultData.entry_pressure_hours || "--"}</div>
|
|
</div>
|
|
</div>
|
|
<div className="result-header rec">
|
|
<div className="result-icon"><Icon type="shijian" /></div>
|
|
<div className="result-content">
|
|
<div className="title">出场压力时段</div>
|
|
<div className="time">{resultData.exit_pressure_hours || "--"}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="result-box">
|
|
<div className="result-box-title">出入场车流量分析</div>
|
|
<Tooltip
|
|
placement="topLeft"
|
|
title={<span>展示所选日期内各时段的车辆出入场数量,用来分析一天内入场车流量与出场车流量的变化情况</span>}
|
|
>
|
|
<i>?</i>
|
|
</Tooltip>
|
|
<ReactEcharts
|
|
option={revenueData}
|
|
style={{ height: "300px", width: "100%", overflow: "hidden" }}
|
|
/>
|
|
</div>
|
|
<div className="result-box">
|
|
<div className="result-box-title">停车饱和度趋势分析</div>
|
|
<Tooltip
|
|
placement="topLeft"
|
|
title={<span>展示所选日期内各个时间段的停车场饱和度变化情况,用来分析停车高峰与低谷时段,入场与出场压力较大的时段</span>}
|
|
>
|
|
<i>?</i>
|
|
</Tooltip>
|
|
<div className="bhd-select">
|
|
<Select
|
|
className="form-con"
|
|
placeholder="请选择"
|
|
defaultValue={'0'}
|
|
options={[
|
|
{
|
|
label: '按时间对比',
|
|
value: '0',
|
|
},
|
|
{
|
|
label: '按计费类型对比',
|
|
value: '1',
|
|
},
|
|
]}
|
|
value={formData.pay_me}
|
|
onChange={(v) =>
|
|
setFormData({ ...formData, pay_me: v })
|
|
}
|
|
/>
|
|
</div>
|
|
<ReactEcharts
|
|
option={parkData}
|
|
style={{ height: "300px", width: "100%", overflow: "hidden" }}
|
|
/>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default ParkingAlyPeriod;
|