Initial commit

This commit is contained in:
Qingtian239113
2022-03-28 17:29:25 +08:00
commit 6afcaa96fe
83 changed files with 3901 additions and 0 deletions

BIN
src/assets/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
src/assets/blue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/blueBorder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/blueTitle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/assets/cardBorder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
src/assets/chain.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/assets/countBorder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
src/assets/green.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/greenBorder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/greyBorder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
src/assets/header.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
src/assets/indigoBorder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/numberBorder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

BIN
src/assets/redTitle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
src/assets/search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

BIN
src/assets/searchWhite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

BIN
src/assets/shape/car.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

BIN
src/assets/shape/other.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

BIN
src/assets/show.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
src/assets/time_box.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/assets/title.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/assets/titleBorder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
src/assets/titleBorder2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
src/assets/titleBorder3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
src/assets/titleBorder4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
src/assets/yellow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/yellowTitle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,380 @@
import style from "./index.less";
import {ScrollBoard} from "@jiaminghi/data-view-react";
import {forwardRef, useEffect, useImperativeHandle, useState} from "react";
import modalApi from "@/https/apis/modalApi";
import rightPageApi from "@/https/apis/rightPageApi";
import {ConfigProvider} from 'antd';
import zh_CN from 'antd/es/locale/zh_CN';
import dayjs from "dayjs";
import DatePicker from "@/components/DatePicker/index";
const {RangePicker} = DatePicker;
const CarModalPage = ({}, ref) => {
const [visible, setVisible] = useState(false);
const [carVisible, setCarVisible] = useState(true)
const [illegalTypeList, setIllegalTypeList] = useState([])
//轮播表
const [illegalList, setIllegalList] = useState([])
const [illegalLists, setIllegalLists] = useState([])
//图片列表
const [imgList, setImgList] = useState([])
//详情弹框的显示
const [detailVisible, setDetailVisible] = useState(false);
//查询参数
const [illegalParams, setIllegalParams] = useState({
vehicleTypeId: "", ruleId: ""
})
//违规的学院
const [collegeList, setCollegeList] = useState([])
const [searchText, setSearchText] = useState('')
const [configDetails, setConfigDetails] = useState({})
useEffect(() => {
carTypeList()
getCollegeList()
whereClick()
}, [])
useEffect(() => {
console.log(illegalParams)
getIllegalList(illegalParams)
getIllegalType(illegalParams.vehicleTypeId)
}, [illegalParams])
useImperativeHandle(ref, () => ({
showModal
}))
///////////////////////////////////// 逻辑方法
/**
* 显示弹窗
*/
const showModal = (params, date) => {
setVisible(true);
whereClick(params, date);
}
/**
* 判断从哪里点
* @param params
* @param date
*/
const whereClick = (params, date) => {
setCarVisible(true)
date = dayjs()
let tempDate = {
startTime: dayjs(),
endTime: dayjs()
}
//圆饼图
if (params?.illegalType) {
setIllegalParams({
...params,
ruleId: params.id,
startTime: tempDate.startTime.add(-7, 'day').format('YYYY-MM-DD'),
endTime: tempDate.endTime.format('YYYY-MM-DD')
})
return
}
//柱状图
if (params?.type) {
setIllegalParams({...params, startTime: '2022-' + params.type, endTime: '2022-' + params.type})
}
//横向图
if (params?.collegeNo) {
setIllegalParams({
...params,
no: params.collegeNo,
startTime: date.format('YYYY-MM') + '-01',
endTime: date.add(1, 'month').format('YYYY-MM') + '-01'
})
}
if (params?.detail) {
console.log(params)
setDetailVisible(true)
setCarVisible(false)
let tempData = {
headerHeight: 50,
headerBGC: 'rgba(47, 253, 239, 0.1)',
oddRowBGC: 'rgba(0,0,0,0)',
evenRowBGC: 'rgba(47, 253, 239, 0.1)',
columnWidth: [150, 145, 155, 155, 50],
index: true,
align: ["center"],
rowNum: 1,
header: [
"<span style='color: #BDFF79;'>车牌号</span>",
"<span style='color: #BDFF79;'>车主</span>",
"<span style='color: #BDFF79;'>执行人</span>",
"<span style='color: #BDFF79;'>扣分</span>",
],
data: [
[
params.name.row[0],
params.name.row[3]?.replace(/<[^>]*>/g, ""),
params.name.row[4]?.replace(/<[^>]*>/g, ""),
params.name.row[2],
],
],
}
setConfigDetails(tempData)
setImgList([
params.name.row[5]?.replace(/<[^>]*>/g, ""),
])
}
//车辆类型
if (params?.vehicleTypeId) {
setIllegalParams({...params})
}
//日期
if (params?.title === '今日') {
tempDate.startTime = tempDate.startTime.format('YYYY-MM-DD')
tempDate.endTime = tempDate.startTime
setIllegalParams({...params, startTime: tempDate.startTime, endTime: tempDate.endTime})
} else if (params?.title === '昨日') {
tempDate.startTime = tempDate.startTime.add(-1, 'day').format('YYYY-MM-DD')
tempDate.endTime = tempDate.startTime
setIllegalParams({...params, startTime: tempDate.startTime, endTime: tempDate.endTime})
} else if (params?.title === '上月') {
tempDate.startTime = tempDate.startTime.add(-1, 'month').format('YYYY-MM') + '-01'
tempDate.endTime = tempDate.endTime.format('YYYY-MM') + '-01'
setIllegalParams({...params, startTime: tempDate.startTime, endTime: tempDate.endTime})
} else if (params?.title === '本月') {
tempDate.startTime = tempDate.startTime.format('YYYY-MM') + '-01'
tempDate.endTime = tempDate.endTime.add(1, 'month').format('YYYY-MM') + '-01'
setIllegalParams({...params, startTime: tempDate.startTime, endTime: tempDate.endTime})
}
}
const getIllegalList = () => {
modalApi.illegalList({
...illegalParams,
keyword: searchText,
startTime: illegalParams?.startTime,
endTime: illegalParams?.endTime
}).then(res => {
if (res.code === 200) {
setIllegalList(res.data.map(it => {
return [
`<span>${it.vehicleType}</span>`,
`<span style='margin-right:50px'>${it.plateNo}</span>`,
`<span> &nbsp ${it.ruleName}</span>`,
`<span> &nbsp ${"&nbsp&nbsp" + it.score}</span>`,
`<span style='display:none'>${it.userName}</span>`,
`<span style='display:none'>${it.reportUserName}</span>`,
`<span style='display:none'>${it.imgs}</span>`,
]
}))
}
})
}
/**
* 点击获取详情
* @param val
*/
const detailClick = (val) => {
console.log(val)
setDetailVisible(true)
let tempData = {
...config, data: [
[
val.row[2],
val.row[5]?.replace(/<[^>]*>/g, ""),
val.row[6]?.replace(/<[^>]*>/g, ""),
val.row[4],
],
],
rowNum: 1,
header: [
"<span style='color: #BDFF79;'>车牌号</span>",
"<span style='color: #BDFF79;'>车主</span>",
"<span style='color: #BDFF79;'>执行人</span>",
"<span style='color: #BDFF79;'>扣分</span>",
],
}
setImgList([
val.row[7]?.replace(/<[^>]*>/g, ""),
val?.row[8]?.replace(/<[^>]*>/g, ""),
])
setConfigDetails(tempData)
}
/**
* 车辆类型列表
* @param key
*/
const carTypeList = (key) => {
modalApi.carTypeList(key).then(res => {
if (res.code === 200) {
setIllegalTypeList(res.data.map(it => {
return {
name: it.name,
value: it.id,
}
}))
}
})
}
/**
* 违规类型列表
* @param key
*/
const getIllegalType = (key) => {
modalApi.illegalTypeList(key).then(res => {
if (res.code === 200) {
setIllegalLists(res.data.map(it => {
return {
name: it.name,
value: it.id,
}
}))
}
})
}
const getCollegeList = () => {
rightPageApi.collegeCount('2022-03-01').then(res => {
if (res.code == 200) {
setCollegeList(res.data.map(it => {
return {
name: it.collegeName,
value: it.collegeNo,
}
}))
}
})
}
//////////////////////////////// 页面渲染
if (!visible) {
return "";
}
let config = {
rowNum: 6, //表格行数
headerHeight: 50,
headerBGC: 'rgba(47, 253, 239, 0.1)',
oddRowBGC: 'rgba(0,0,0,0)',
evenRowBGC: 'rgba(47, 253, 239, 0.1)',
columnWidth: [150, 145, 155, 155, 50],
index: true,
align: ["center"],
header: [
"<span style='color: #BDFF79;'>车型</span>",
"<span style='color: #BDFF79;'>车牌</span>",
"<span style='color: #BDFF79;'>违规</span>",
"<span style='color: #BDFF79;'>扣分</span>",
],
data: illegalList,
}
//
return (
<div>
{/*搜索弹框*/}
{carVisible &&
<div className={style.modal_box}>
<div className={style.modal_title}>
<span className={style.title_box}>车辆违规-查询</span>
<span style={{position: 'absolute', right: 10, cursor: 'pointer'}}
onClick={() => {
setIllegalParams({})
setVisible(false)
}}>×</span>
</div>
<div className={style.modal_body}>
<select className={style.text} type="text" id="carType" value={illegalParams.vehicleTypeId}
onChange={(e) =>
setIllegalParams({...illegalParams, vehicleTypeId: e.target.value})}>
<option value="" style={{display: 'none'}}>违规车型</option>
{illegalTypeList.map(it => {
return (<option key={it.value} value={it.value}>{it.name}</option>)
})}
</select>
<img src={require("@/assets/chain.png")} alt=""/>
<select className={style.text} value={illegalParams.ruleId}
onChange={(e) =>
setIllegalParams({...illegalParams, ruleId: e.target.value})}
>
<option value="" style={{display: 'none'}}>违规类型</option>
{illegalLists.map(it => {
return (<option key={it.value} value={it.value}>{it.name}</option>)
})}
</select>
<img src={require("@/assets/chain.png")} alt=""/>
<input className={style.text} placeholder="输入车牌" type="search"
onChange={e => setSearchText(e.target.value)}/>
<div className={style.search_btn}>
<img style={{position: "absolute", left: '25%', top: '25%', width: 25, height: 25}}
src={require("@/assets/searchWhite.png")} onClick={() => getIllegalList()}/>
</div>
</div>
<div className={style.modal_body} style={{height: '5%', marginBottom: 20}}>
<img src={require("@/assets/chain.png")} alt=""/>
<ConfigProvider locale={zh_CN}>
<RangePicker className={style.dataPicker} locale={zh_CN} allowClear={false} onChange={(e) => {
setIllegalParams({
...illegalParams,
startTime: e[0].format('YYYY-MM-DD'),
endTime: e[1].format('YYYY-MM-DD'),
})
}}
value={[illegalParams.startTime ? dayjs(illegalParams.startTime) : '', illegalParams.endTime ? dayjs(illegalParams.endTime) : '']}
/>
</ConfigProvider>
<img src={require("@/assets/chain.png")} alt=""/>
<select className={style.text} style={{width: '34%'}} id="carType" value={illegalParams.no}
onChange={(e) =>
setIllegalParams({...illegalParams, no: e.target.value})}>
<option value="" style={{display: 'none'}}>违规学院</option>
{collegeList.map(it => {
return (<option key={it.value} value={it.value}>{it.name}</option>)
})}
</select>
<img src={require("@/assets/chain.png")} alt=""/>
</div>
{/* 轮播表 */}
{illegalList && <div style={{cursor: 'pointer'}}>
<ScrollBoard onClick={(e) => detailClick(e)} config={config} style={{width: '100%', height: '300px'}}/>
</div>
}
</div>
}
{/* 详情弹框 */}
{
detailVisible &&
<div className={style.modal_box} style={{width: 769, height: 420}}>
<div className={style.modal_title}>
<span className={style.title_box}>车辆违规-详情</span>
<span style={{position: 'absolute', right: 10, cursor: 'pointer'}}
onClick={() => setDetailVisible(false)}>×</span>
</div>
<div className={style.middleDetails}>
{imgList[0] && <div className={style.pic}>
<img src={imgList[0]} alt="" style={{width: '100%', height: '100%'}}/>
</div>}
{imgList[1] &&
<div className={style.pic}>
<img src={imgList[1]} alt="" style={{width: '100%', height: '100%'}}/>
</div>}
</div>
<div>
<ScrollBoard config={configDetails} style={{height: 100}}/>
</div>
</div>
}
</div>
)
}
export default forwardRef(CarModalPage)

View File

@@ -0,0 +1,118 @@
.modal_box {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -75%);
width: 769px;
height: 500px;
background: rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
border: 2px solid #10FDF2;
backdrop-filter: blur(10px);
.dataPicker {
height: 45px;
width: 40%;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
border-radius: 22px;
background-color: rgba(0, 0, 0, 0.1);
border: 1px solid #14FDF7;
font-size: 14px;
font-weight: 400;
color: #FFFFFF;
line-height: 20px;
text-align: center;
padding-left: 15px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
}
.modal_title {
line-height: 36px;
font-size: 20px;
font-weight: 500;
color: #FFFFFF;
text-align: center;
//text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
//background: linear-gradient(180deg, #10FDF2 0%, rgba(16, 253, 242, 0.5) 100%);
//box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.25);
backdrop-filter: blur(10px);
}
.modal_body {
display: flex;
justify-content: space-around;
align-items: center;
height: 24%;
color: #40a9ff;
//background-color: #40a9ff;
.text {
height: 45px;
width: 25%;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
border-radius: 22px;
background-color: rgba(0, 0, 0, 0.1);
border: 1px solid #14FDF7;
font-size: 14px;
font-weight: 400;
color: #D5D8E2;
line-height: 20px;
text-align: center;
padding-left: 10px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
}
.title_box {
display: block;
font-size: 20px;
background-color: #10FDF2;
padding: 0 8px 5px 8px;
}
.search_btn {
position: absolute;
right: 10px;
width: 43px;
height: 43px;
border-radius: 50%;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
cursor: pointer;
background: #14FDF7;
}
option {
font-size: 14px;
// background: rgba(47, 253, 239, 0.1);
background-color: rgb(0, 52, 73);
color: #D5D8E2;
backdrop-filter: blur(10px);
border: 2px solid #24A7A4;
}
}
.middleDetails {
display: flex;
justify-content: space-around;
align-items: center;
height: 68%;
.pic {
width: 45%;
height: 200px;
border: 1px solid #14FDF7;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
}
}
}

View File

@@ -0,0 +1,8 @@
import { Dayjs } from 'dayjs';
import dayjsGenerateConfig from 'rc-picker/es/generate/dayjs';
import generatePicker from 'antd/es/date-picker/generatePicker';
import 'antd/es/date-picker/style/index';
const DatePicker = generatePicker<Dayjs>(dayjsGenerateConfig);
export default DatePicker;

20
src/components/Flex/index.d.ts vendored Normal file
View File

@@ -0,0 +1,20 @@
import * as React from 'react';
export interface IFlexProps {
direction?: 'row' | 'column' | 'row-reverse' | 'column-reverse';
wrap?: 'nowrap' | 'wrap' | 'wrap-reverse';
justify?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around';
alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch';
itemSelf?: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'stretch';
itemOrder?: number;
itemGrow?: number;
style?: object;
className?: string;
onClick?: any,
}
export default class Flex extends React.Component<IFlexProps, any> {
}

View File

@@ -0,0 +1,56 @@
import React, {memo} from "react";
import styles from './index.less';
const Index = memo((props) => {
const {
direction, wrap, justify, alignItems,
itemOrder, itemSelf, itemGrow, onClick,
className, style, ...other
} = props;
let newStyle = {};
if (direction) {
newStyle['flexDirection'] = direction;
newStyle['WebkitFlexDirection'] = direction;
}
if (wrap) {
newStyle['flexWrap'] = wrap;
newStyle['WebkitFlexWrap'] = wrap;
}
if (justify) {
newStyle['justifyContent'] = justify;
newStyle['WebkitJustifyContent'] = justify;
}
if (alignItems) {
newStyle['alignItems'] = alignItems;
newStyle['WebkitAlignItems'] = alignItems;
}
if (itemSelf) {
newStyle['alignSelf'] = itemSelf;
newStyle['WebkitAlignSelf'] = itemSelf;
}
if (itemOrder) {
newStyle['order'] = itemOrder;
}
if (itemGrow) {
newStyle['flexGrow'] = itemGrow;
newStyle['WebkitFlexGrow'] = itemGrow;
}
let clazz = styles.flex;
if (className) {
clazz = clazz + ' ' + className;
}
return <div
{...other} className={clazz} style={{...newStyle, ...style}} onClick={onClick}
>{props.children}</div>
});
const Flex = (props) => {
return <Index {...props} />
};
export default Flex;

View File

@@ -0,0 +1,15 @@
.flex {
display: -webkit-flex; /* Safari */
display: flex;
position: relative;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
word-break: break-all;
white-space: normal;
flex-shrink: 0;
-webkit-flex-shrink: 0;
}

View File

@@ -0,0 +1,114 @@
/*
* 翻牌数字
* @author 兔子先生
* @createDate: 2019-11-24
*/
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import './flipper.css'
class Flipper extends Component {
constructor(props) {
super(props)
this.state = {
isFlipping: false,
flipType: 'down',
frontTextFromData: 0,
backTextFromData: 1
}
}
render() {
const {isFlipping, flipType, frontTextFromData, backTextFromData} = this.state
return (
<div className={['M-Flipper', flipType, isFlipping ? 'go' : null].join(' ')}>
<div className={'digital front ' + this._textClass(frontTextFromData)}/>
<div className={'digital back ' + this._textClass(backTextFromData)}/>
</div>
)
}
// componentDidMount() {
// this.props.onRef(this)
// }
test() {
console.log('test')
}
_textClass(number) {
return 'number' + number
}
_flip(type, front, back) {
// 如果处于翻转中,则不执行
if (this.isFlipping) {
return false
}
this.setState({
frontTextFromData: front,
backTextFromData: back,
// 根据传递过来的type设置翻转方向
flipType: type,
// 设置翻转状态为true
isFlipping: true
})
setTimeout(() => {
this.setState({
frontTextFromData: back,
isFlipping: false
})
}, this.props.duration)
}
// 下翻牌
flipDown(front, back) {
this._flip('down', front, back)
}
// 上翻牌
flipUp(front, back) {
this._flip('up', front, back)
}
// 设置前牌文字
setFront(text) {
this.setState({
frontTextFromData: text
})
}
// 设置后牌文字
setBack(text) {
this.setState({
backTextFromData: text
})
}
}
// props类型校验
Flipper.propTypes = {
frontText: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string
]),
backText: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string
]),
duration: PropTypes.number
}
// props默认值
Flipper.defaultProps = {
// front paper text
// 前牌文字
frontText: 0,
// back paper text
// 后牌文字
backText: 1,
// flipping duration, please be consistent with the CSS animation-duration value.
// 翻牌动画时间与CSS中设置的animation-duration保持一致
duration: 600
}
export default Flipper

View File

@@ -0,0 +1,186 @@
.M-Flipper {
display: inline-block;
position: relative;
width: 40px;
margin: 2px;
height: 70px;
line-height: 70px;
border: solid 1px #000;
border-radius: 10px;
background: #666666;
font-size: 60px;
color: #fff;
box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
text-align: center;
font-family: 'Helvetica Neue';
}
.M-Flipper .digital:before,
.M-Flipper .digital:after {
content: '';
position: absolute;
left: 0;
right: 0;
overflow: hidden;
box-sizing: border-box;
background: #000 url("../../assets/numberBorder.png");
}
.M-Flipper .digital:before {
top: 0;
bottom: 50%;
border-radius: 10px 10px 0 0;
border-bottom: solid 1px #666;
}
.M-Flipper .digital:after {
top: 50%;
bottom: 0;
border-radius: 0 0 10px 10px;
line-height: 0;
}
/*向下翻*/
.M-Flipper.down .front:before {
z-index: 3;
}
.M-Flipper.down .back:after {
z-index: 2;
transform-origin: 50% 0%;
transform: perspective(160px) rotateX(180deg);
}
.M-Flipper.down .front:after,
.M-Flipper.down .back:before {
z-index: 1;
}
.M-Flipper.down.go .front:before {
transform-origin: 50% 100%;
animation: frontFlipDown 0.6s ease-in-out both;
box-shadow: 0 -2px 6px rgba(255, 255, 255, 0.3);
backface-visibility: hidden;
}
.M-Flipper.down.go .back:after {
animation: backFlipDown 0.6s ease-in-out both;
}
/*向上翻*/
.M-Flipper.up .front:after {
z-index: 3;
}
.M-Flipper.up .back:before {
z-index: 2;
transform-origin: 50% 100%;
transform: perspective(160px) rotateX(-180deg);
}
.M-Flipper.up .front:before,
.M-Flipper.up .back:after {
z-index: 1;
}
.M-Flipper.up.go .front:after {
transform-origin: 50% 0;
animation: frontFlipUp 0.6s ease-in-out both;
box-shadow: 0 2px 6px rgba(255, 255, 255, 0.3);
backface-visibility: hidden;
}
.M-Flipper.up.go .back:before {
animation: backFlipUp 0.6s ease-in-out both;
}
@keyframes frontFlipDown {
0% {
transform: perspective(160px) rotateX(0deg);
}
100% {
transform: perspective(160px) rotateX(-180deg);
}
}
@keyframes backFlipDown {
0% {
transform: perspective(160px) rotateX(180deg);
}
100% {
transform: perspective(160px) rotateX(0deg);
}
}
@keyframes frontFlipUp {
0% {
transform: perspective(160px) rotateX(0deg);
}
100% {
transform: perspective(160px) rotateX(180deg);
}
}
@keyframes backFlipUp {
0% {
transform: perspective(160px) rotateX(-180deg);
}
100% {
transform: perspective(160px) rotateX(0deg);
}
}
.M-Flipper .number0:before,
.M-Flipper .number0:after {
content: '0';
}
.M-Flipper .number1:before,
.M-Flipper .number1:after {
content: '1';
}
.M-Flipper .number2:before,
.M-Flipper .number2:after {
content: '2';
}
.M-Flipper .number3:before,
.M-Flipper .number3:after {
content: '3';
}
.M-Flipper .number4:before,
.M-Flipper .number4:after {
content: '4';
}
.M-Flipper .number5:before,
.M-Flipper .number5:after {
content: '5';
}
.M-Flipper .number6:before,
.M-Flipper .number6:after {
content: '6';
}
.M-Flipper .number7:before,
.M-Flipper .number7:after {
content: '7';
}
.M-Flipper .number8:before,
.M-Flipper .number8:after {
content: '8';
}
.M-Flipper .number9:before,
.M-Flipper .number9:after {
content: '9';
}

View File

@@ -0,0 +1,65 @@
import React, {Fragment, useState, useRef, useEffect} from 'react'
import Flipper from './Flipper'
import './flipper.css'
const FlipView = ({number = '0'}) => {
const [lastArray, setLastArray] = useState(['0', '0', '0', '0', '0', '0']);
const flipper0 = useRef();
const flipper1 = useRef();
const flipper2 = useRef();
const flipper3 = useRef();
const flipper4 = useRef();
const flipper5 = useRef();
const flipper6 = useRef();
useEffect(() => {
run();
}, [number]);
///////////////////////////////////////// 逻辑方法
/**
* 开始翻牌
*/
const run = () => {
let tempArray = new Array(6 - number.length);
let array = tempArray.concat([...number.split('')]);
const flipDown = (front, back, ref) => {
front = front || "0";
back = back || "0";
if (front != back) {
ref && ref.current.flipDown(back, front);
}
};
flipDown(array[0], lastArray[0], flipper1);
flipDown(array[1], lastArray[1], flipper2);
flipDown(array[2], lastArray[2], flipper3);
flipDown(array[3], lastArray[3], flipper4);
flipDown(array[4], lastArray[4], flipper5);
flipDown(array[5], lastArray[5], flipper6);
setLastArray(array);
};
///////////////////////////////////////// 页面渲染
return <Fragment>
<Flipper ref={flipper0}/>
<em style={{fontSize: 36, margin: '0 5px'}}>,</em>
<Flipper ref={flipper1}/>
<Flipper ref={flipper2}/>
<Flipper ref={flipper3}/>
<em style={{fontSize: 36, margin: '0 5px'}}>,</em>
<Flipper ref={flipper4}/>
<Flipper ref={flipper5}/>
<Flipper ref={flipper6}/>
</Fragment>
};
export default FlipView;

View File

@@ -0,0 +1,270 @@
import React, {useEffect, useState} from "react";
import style from './index.less'
import {ScrollBoard} from "@jiaminghi/data-view-react";
import leftPageApi from "@/https/apis/leftPageApi";
import yellowPng from '@/assets/yellow.png';
import Flex from "@/components/Flex";
import TitleView from "@/components/TitleView";
import FlipView from "@/components/FlipView";
const border = [require('@/assets/blueBorder.png'), require('@/assets/greenBorder.png'), require('@/assets/indigoBorder.png')]
const typeCount = [require('@/assets/shape/car.png'), require('@/assets/shape/electricCars.png'), require('@/assets/shape/other.png')]
const LeftPage = ({onCarIllegalClick, onUserIllegalClick}) => {
const {bottom_box, title_box2} = style;
const [carType, setCarType] = useState([])
const [currentMonth, setCurrentMonth] = useState([])
const [lastMonth, setLastMonth] = useState([])
const [currentDay, setCurrentDay] = useState([])
const [lastDay, setLastDay] = useState([])
const [illegalList, setIllegalList] = useState([])
//查询参数
const [searchText, setSearchText] = useState('')
useEffect(() => {
getCountType()
getMonthCount()
getLastMonthCount()
getDayCount()
getLastCount()
getIllegalList()
}, [])
///////////////////////////////////////// 逻辑方法
const getCountType = () => {
leftPageApi.countType().then(res => {
if (res.code === 200) {
res.data.forEach((it, index) => {
it.icon = typeCount[index];
it.border = border[index];
});
setCarType(res.data)
}
})
}
const getMonthCount = () => {
leftPageApi.monthCount().then(res => {
if (res.code == 200) {
setCurrentMonth(res.data)
}
})
}
const getLastMonthCount = () => {
leftPageApi.lastMonthCount().then(res => {
if (res.code == 200) {
setLastMonth(res.data)
}
})
}
const getDayCount = () => {
leftPageApi.dayCount().then(res => {
if (res.code == 200) {
setCurrentDay(res.data)
}
})
}
const getLastCount = () => {
leftPageApi.lastDay().then(res => {
if (res.code == 200) {
setLastDay(res.data)
}
})
}
const getIllegalList = () => {
leftPageApi.illegalList().then(res => {
if (res.code == 200) {
// console.log(res.data)
setIllegalList(res.data.map(it => {
return [it.plateNo, it.ruleName, it.score,
`<span style='display:none'>${it.userName}</span>`,
`<span style='display:none'>${it.reportUserName}</span>`,
`<span style='display:none'>${it.imgs}</span>`
]
}))
}
})
}
///////////////////////////////////////// 数据常量
let config = {
headerBGC: 'rgba(47, 253, 239, 0.1)',
headerHeight: 36,
oddRowBGC: 'rgba(0, 0, 0, 0)',
evenRowBGC: 'rgba(47, 253, 239, 0.1)',
columnWidth: [180, 85, 50],
header: [
"<span style='color: #BDFF79;'>车牌号</span>",
"<span style='color: #BDFF79;'>违规</span>",
"<span style='color: #BDFF79;'>扣分</span>",
],
data: illegalList
};
let counts = [
{
title: '昨日',
count: lastDay,
imgSrc: yellowPng,
},
{
title: '今日',
count: currentDay,
imgSrc: "./imgs/blue.png",
},
{
title: '本月',
count: currentMonth,
imgSrc: require('@/assets/light.png'),
},
{
title: '上月',
count: lastMonth,
imgSrc: require('@/assets/green.png'),
},
];
return <Flex
direction={"column"} className={style.container} justify={"center"} alignItems={"center"}
>
<Flex className={style.carCount} direction={"column"}>
<TitleView
title={'车辆统计管理'} bgUrl={'./imgs/titleBorder.png'} bgStyle={{width: 176, height: 53}}
extra={<Flex
itemGrow={1} style={{width: 0}} alignItems={"center"} justify={"flex-end"}
className={style.title_box}
>
<input
className={style.text} placeholder="人员查询" type="search"
onChange={e => setSearchText(e.target.value)}
/>
<div className={style.search_btn} onClick={onUserIllegalClick}>
<img
alt={''} style={{position: "absolute", left: '25%', top: '25%', width: 25, height: 25}}
src={require("@/assets/searchWhite.png")}
/>
</div>
</Flex>}
/>
<Flex className={style.inoutCar} justify={"space-between"}>
<Flex style={{width: 187, marginRight: 6}}>
<Flex itemGrow={1} style={{width: 0}}>
<img alt={''} style={{position: 'absolute'}} src={require("@/assets/countBorder.png")}/>
<img alt={''} style={{position: 'absolute', top: 20, left: 26}}
src={require("@/assets/shape/inSchool.png")}/>
<img alt={''} style={{position: 'absolute', top: 10, left: 10}} src={require("@/assets/show.png")}/>
</Flex>
<Flex direction={"column"} justify={"center"} alignItems={"center"} style={{marginRight: 12}}>
<Flex style={{fontSize: 18, fontWeight: 500, color: '#E9CC7C'}}>156</Flex>
<Flex style={{fontSize: 14}}>进校车辆</Flex>
</Flex>
</Flex>
<Flex style={{width: 187, marginLeft: 6}}>
<Flex itemGrow={1} style={{width: 0}}>
<img alt={''} style={{position: 'absolute'}} src={require("@/assets/countBorder.png")}/>
<img alt={''} style={{position: 'absolute', top: 20, left: 26}}
src={require("@/assets/shape/outSchool.png")}/>
<img alt={''} style={{position: 'absolute', top: 10, left: 10}} src={require("@/assets/show.png")}/>
</Flex>
<Flex direction={"column"} justify={"center"} alignItems={"center"} style={{marginRight: 12}}>
<Flex style={{fontSize: 18, fontWeight: 500, color: '#E9CC7C'}}>143</Flex>
<Flex style={{fontSize: 14}}>出校车辆</Flex>
</Flex>
</Flex>
</Flex>
<Flex className={style.carType} justify={"space-between"}>
{carType.map((it, index) => {
return <Flex
key={index} className={style.carBox} direction={"column"} justify={"center"} alignItems={"center"}
onClick={() => onCarIllegalClick(it)}
>
<img alt={''} style={{position: 'absolute', zIndex: 0, left: 0}} src={it.border}/>
<img alt='' style={{zIndex: 1}} src={it.icon}/>
<Flex style={{zIndex: 1, fontSize: 18, fontWeight: 500, color: '#E9CC7C'}}>{it.count}</Flex>
<Flex style={{zIndex: 1, fontSize: 14}}>{it.vehicleType}</Flex>
</Flex>
})}
</Flex>
<Flex className={style.carEvent} justify={"space-between"}>
<Flex
className={style.body_box_down} alignItems={"center"}
onClick={() => onCarIllegalClick()}
>
<img alt={''} style={{position: 'absolute'}} src={require("@/assets/cardBorder.png")}/>
<span style={{paddingLeft: 22}}>车辆违规</span>
<img alt={''} className={style.img_search} src={require("@/assets/search.png")}/>
</Flex>
<Flex className={style.body_box_down} alignItems={"center"}>
<img alt={''} style={{position: 'absolute'}} src={require("@/assets/greyBorder.png")}/>
<span style={{paddingLeft: 28}}>事件上报</span>
</Flex>
<Flex className={style.body_box_down} alignItems={"center"}>
<img alt={''} style={{position: 'absolute'}} src={require("@/assets/greyBorder.png")}/>
<span style={{paddingLeft: 28}}>扣分记录</span>
</Flex>
</Flex>
</Flex>
{/*轮播表*/}
<Flex itemGrow={1} style={{height: 0, marginBottom: 20}}>
<ScrollBoard
config={config} onClick={e => onCarIllegalClick({name: e, detail: true})}
/>
</Flex>
{/*底部盒子*/}
<Flex className={style.bottom_box} direction={"column"}>
<TitleView
bgUrl={'./imgs/titleBorder2.png'} title={'违规总数统计'} bgStyle={{width: 176, height: 53}}
/>
{/* 翻牌器 */}
<Flex style={{margin: '10px 0', width: '100%'}} justify={"center"}>
<FlipView number={'112'}/>
</Flex>
{/* 违规数统计 */}
<Flex className={style.dayStat} wrap={"wrap"} justify={"space-around"}>
{counts.map((it, index) => {
return (
<Flex
key={index} className={style.down_body_box} alignItems={"center"} justify={"center"}
onClick={(e) => onCarIllegalClick(it)}
>
<img alt={''} src={it.imgSrc} style={{position: "absolute", zIndex: 0}}/>
<span style={{zIndex: 1, fontSize: 32, lineHeight: 1}}>{it.count}</span>
<span style={{zIndex: 1, fontSize: 14}}>{it.title}</span>
</Flex>
)
})}
</Flex>
</Flex>
</Flex>
}
export default LeftPage

View File

@@ -0,0 +1,244 @@
.container {
width: 400px;
background-color: rgba(0, 0, 0, 0.1);
.carCount {
width: 100%;
.title_box {
height: 80px;
font-size: 18px;
.titleBg {
width: 176px;
height: 53px;
background-size: contain;
background-repeat: no-repeat;
}
.text {
height: 45px;
width: 160px;
border-radius: 22px;
background-color: rgba(0, 0, 0, 0.1);
border: 1px solid #14FDF7;
font-size: 14px;
font-weight: 400;
color: #D5D8E2;
line-height: 20px;
padding-left: 20px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
outline: none;
}
.search_btn {
position: absolute;
right: 0;
width: 43px;
height: 43px;
border-radius: 50%;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
cursor: pointer;
background: #14FDF7;
}
}
.inoutCar {
height: 75px;
margin-bottom: 20px;
}
.carType {
height: 75px;
margin-bottom: 20px;
.carBox {
width: 120px;
height: 75px;
font-size: 16px;
z-index: 0;
}
}
.carEvent {
height: 50px;
margin-bottom: 20px;
}
.body_box_down {
width: 120px;
height: 50px;
font-size: 15px;
cursor: pointer;
span {
margin-top: 8px;
}
.img_search {
margin-top: 8px;
margin-left: 6px;
}
}
}
}
.bottom_box {
color: white;
.title_box2 {
height: 30%;
position: relative;
}
.drawNumber {
font-size: 24px;
}
.dayStat {
.down_body_box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
width: 170px;
height: 70px;
cursor: pointer;
margin-bottom: 20px;
img {
width: 100%;
height: 100%;
}
&:hover {
transition: all 0.3s;
}
&:nth-child(1) {
color: rgb(255, 236, 142);
}
&:nth-child(2) {
color: rgb(75, 242, 255);
}
&:nth-child(3) {
color: rgb(119, 255, 229);
}
&:nth-child(4) {
color: rgb(106, 255, 125);
}
}
}
}
.modal_box {
position: absolute;
left: 50%;
top: 29%;
transform: translate(78%, 0);
width: 769px;
height: 430px;
background: rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
border: 2px solid #10FDF2;
backdrop-filter: blur(10px);
.modal_title {
transform: translateY(5%);
height: 8%;
font-size: 18px;
font-weight: 500;
color: #FFFFFF;
line-height: 25px;
text-align: center;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
}
.modal_body {
display: flex;
justify-content: space-around;
align-items: center;
height: 22%;
.text {
height: 45px;
width: 25%;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
border-radius: 22px;
background-color: rgba(0, 0, 0, 0.1);
border: 1px solid #14FDF7;
font-size: 14px;
font-weight: 400;
color: #D5D8E2;
line-height: 20px;
text-align: center;
padding-left: 10px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
&:nth-child(1) {
//transform: translateX(55px);
}
}
.title_box {
display: block;
font-size: 20px;
background-color: #10FDF2;
padding: 0 8px 5px 8px;
}
.search_btn {
position: absolute;
right: 15px;
width: 43px;
height: 43px;
border-radius: 50%;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
cursor: pointer;
background: #14FDF7;
}
option {
font-size: 14px;
// background: rgba(47, 253, 239, 0.1);
background-color: rgb(0, 52, 73);
color: #D5D8E2;
backdrop-filter: blur(10px);
border: 2px solid #24A7A4;
}
}
.middleDetails {
display: flex;
justify-content: space-around;
align-items: center;
height: 55%;
.pic {
width: 35%;
height: 205px;
border: 1px solid #14FDF7;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
}
}
.board_box {
height: 200px;
cursor: pointer;
&:hover {
}
}
}

View File

@@ -0,0 +1,26 @@
import style from './index.less'
const middlePage = () => {
return (
<div className={style.container}>
<div className={style.title_box}>
<img style={{position:"absolute"}} src={require('@/assets/title.png')}/>
<span >异常分部预览</span>
</div>
<div className={style.type_title}>
<img style={{position:"absolute"}} src={require('@/assets/redTitle.png')}/>
<span>汽车超速</span>
</div>
<div className={style.type_title}>
<img style={{position:"absolute"}} src={require('@/assets/yellowTitle.png')}/>
<span>汽车超载</span>
</div>
<div className={style.type_title}>
<img style={{position:"absolute"}} src={require('@/assets/blueTitle.png')}/>
<span>汽车违停</span>
</div>
</div>
)
}
export default middlePage

View File

@@ -0,0 +1,39 @@
.container {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
width: 800px;
color: #ffffff;
margin-left: 100px;
//background-color: chocolate;
.title_box {
position: relative;
width: 273px;
height: 53px;
font-size: 18px;
span {
position: absolute;
top: 12px;
left: 75px;
}
}
.type_title {
position: relative;
width: 168px;
height: 32px;
span {
position: absolute;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
font-size: 14px;
top: 5px;
left: 42px;
}
}
}

View File

@@ -0,0 +1,76 @@
import {useEffect, useRef, useState} from "react";
import rightPageApi from "@/https/apis/rightPageApi";
const {Bar} = window.charts;
const DemoBar = ({requestDate,onClick}) => {
const [list,setList] = useState([])
const pieRef = useRef();
useEffect(()=>{
getCollegeCount()
clickPie(pieRef?.current?.getChart())
},[requestDate])
const getCollegeCount = () => {
rightPageApi.collegeCount(requestDate.format("YYYY-MM-DD")).then(res=>{
if(res.code == 200){
setList(res.data.map(it => {
return {collegeName:it.collegeName.substr(0,10), sales: parseInt(it.count,10),collegeNo:it.collegeNo}
}))
}
})
}
const clickPie = (plot) => {
plot.off("plot:click");
plot.on('plot:click', (...args) => {
onClick && onClick(args[0]?.data?.data,requestDate.format('YYYY-MM'))
})
}
const data = list
const config = {
data,
xField: 'sales',
yField: 'collegeName',
color: '#3B90FF',
xAxis:{
label:{
style:{
fill:'#F2F2F2'
}
}
},
barStyle: {
fill: 'l(180) 0:#3B90FF 1:rgba(47, 253, 239, 0)',
},
yAxis:{
label:{
style:{
fill:'#F2F2F2'
}
}
},
meta: {
collegeName: {
alias: '类别',
},
sales: {
alias: '违规总数',
},
no:{
alias:'编号'
}
},
minBarWidth: 25,
maxBarWidth: 25,
};
return <Bar ref={pieRef} {...config}/>;
};
export default DemoBar

View File

@@ -0,0 +1,77 @@
import rightPageApi from "@/https/apis/rightPageApi";
import {useEffect, useState} from "react";
const { Column } = window.charts;
const DemoColumn = ({onClick}) => {
const [dataCount,setDataCount] = useState([])
useEffect(()=>{
getSevenCount()
},[])
const getSevenCount = () => {
rightPageApi.sevenCount().then(res=>{
if(res.code == 200){
setDataCount(res.data.map(it=>{
return {type:it.week.substr(5,5),sales:parseInt(it.count,10)}
}))
}
})
}
const clickColumn = (plot) => {
plot.on('plot:click', (...args) => {
onClick && onClick(args[0]?.data?.data)
})
}
const data = dataCount;
const config = {
data,
xField: 'type',
yField: 'sales',
color: '#4AE7E0',
label: {
// 可手动配置 label 数据标签位置
position: 'middle',
// 'top', 'bottom', 'middle',
// 配置样式
style: {
fill: '#F2F2F2',
},
},
xAxis: {
name:'type',
label: {
autoHide: true,
autoRotate: false,
style: {
fill: '#F2F2F2'
}
},
},
barStyle: {
fill: 'red',
},
yAxis:{
label:{
style:{
fill:'#F2F2F2'
}
}
},
meta: {
type: {
alias: '类别',
},
sales: {
alias: '违规数',
},
},
};
return <Column {...config} onReady={clickColumn}/>;
};
export default DemoColumn

View File

@@ -0,0 +1,82 @@
import rightPageApi from "@/https/apis/rightPageApi";
import {useEffect, useState} from "react";
const {Pie, G2} = window.charts;
const G = G2.getEngine('canvas');
const cfg = {
appendPadding: 10,
data: [],
angleField: 'count',
colorField: 'type',
radius: 0.75,
legend: false,
label: {
type: 'spider',
labelHeight: 40,
formatter: (data, mappingData) => {
const group = new G.Group({});
group.addShape({
type: 'circle',
attrs: {
x: 0,
y: 0,
width: 40,
height: 50,
r: 5,
fill: mappingData.color,
},
});
group.addShape({
type: 'text',
attrs: {
x: 10,
y: 8,
text: `${data.type}`,
fill: mappingData.color,
},
});
return group;
},
},
interactions: [
{
type: 'element-selected',
},
{
type: 'element-active',
},
],
};
const DemoPie = ({onClick}) => {
useEffect(() => {
getSevenType()
}, [])
const [dataCount, setDataCount] = useState([])
const clickPie = (plot) => {
plot.on('plot:click', (...args) => {
console.log(args[0]?.data?.data)
onClick && onClick(args[0]?.data?.data)
})
}
const getSevenType = () => {
rightPageApi.sevenType().then(res => {
if (res.code === 200) {
console.log(res.data)
setDataCount(res.data
.map(it => (
{...it, illegalType: it.type, count: parseInt(it.count, 10)}
))
)
}
})
}
return <Pie {...cfg} data={dataCount} onReady={clickPie}/>;
};
export default DemoPie

View File

@@ -0,0 +1,72 @@
import React, {useEffect, useState} from "react";
import style from './index.less'
import BarChart from './BarChart'
import ColumnChart from './ColumnChart'
import PieChart from './PieChart'
import dayjs from "dayjs";
import Flex from "@/components/Flex";
const monthList = []
for (let i = 1; i < 13; i++) {
monthList.push({
name: i + '月',
value: i
})
}
const rightPage = ({onPieClick}) => {
const [dateNum, setDate] = useState(dayjs())
const [copyDate, setCopyDate] = useState(dayjs())
const [dataBoxVisible, setDataBoxVisible] = useState(false)
const {middle_box, bottom_box, title_box} = style;
///////////////////////////////////////// 页面渲染
return <Flex direction={"column"} className={style.container}>
<div className={style.top_box}>
<div className={style.title_box}>
<img style={{left: 10, height: '56px', position: 'absolute'}}
src={require('@/assets/titleBorder3.png')}/>
<span style={{position: "absolute", left: '4vh', top: 12, fontSize: '18px'}}>学院违章排行</span>
<div onClick={() => setDataBoxVisible(!dataBoxVisible)} className={style.time_box}>{copyDate.format("YYYY-MM")}
</div>
</div>
<div style={{height: 300}}><BarChart requestDate={copyDate} onClick={onPieClick}/></div>
</div>
<div className={middle_box}>
<div className={title_box}>
<img style={{left: 20, width: '178px', height: '56px', position: 'absolute'}}
src={require('@/assets/titleBorder4.png')}/>
<span style={{position: "absolute", left: '4.5vh', top: 14, fontSize: '18px'}}>车辆违规统计</span>
</div>
<div style={{height: 300}}><ColumnChart onClick={(args) => onPieClick(args)}/></div>
</div>
<div className={bottom_box} style={{height: 300}}><PieChart onClick={(args) => onPieClick(args)}/></div>
{
dataBoxVisible && <div className={style.dataBox}>
<div className={style.data_title}>
<span style={{fontSize: 26, cursor: 'pointer'}}
onClick={() => setDate(dateNum.add(-1, 'year'))}>&laquo;</span>
<span style={{lineHeight: 2.5}}>{dateNum.format('YYYY')}</span>
<span style={{fontSize: 26, cursor: 'pointer'}}
onClick={() => setDate(dateNum.add(1, 'year'))}>&raquo;</span>
</div>
<div className={style.data_body}>
{
monthList.map(it => {
return <div className={style.data_box} onClick={(e) => {
setDataBoxVisible(false)
setCopyDate(dayjs(dateNum.format("YYYY-") + it.value + "-01"));
}} key={it.value}>{it.name}</div>
})
}
</div>
</div>
}
</Flex>
}
export default rightPage

View File

@@ -0,0 +1,213 @@
.container {
width: 400px;
height: 100%;
color:#fff;
font-size:16px;
background-color: rgba(0,0,0,0.1);
.top_box {
width: 100%;
height: 35%;
padding: 18px;
.title_box {
position: relative;
height:18%;
overflow: hidden;
.time_box {
text-align: center;
position: absolute;
right: 15px;
padding: 1px 8px 1.5px 8px;
font-size: 16px;
overflow: hidden;
transform: translateY(10px);
background-size: 100%;
background: url("../../assets/time_box.png") no-repeat;
cursor: pointer;
&:hover {
background-color: rgba(169,169,169,.2);
transition: all 0.2s;
}
}
}
}
.middle_box {
position: relative;
width: 100%;
height: 35%;
padding: 18px;
//background-color: white;
.title_box {
position: relative;
height:18%;
.time_box {
position: absolute;
width:80px;
height:30px;
transform: translateY(10px);
}
}
.middle_box {
height: 20%;
background-color: chocolate;
}
//background-color: saddlebrown;
}
.bottom_box {
width: 100%;
height: 30%;
//background-color: crimson;
padding: 18px;
}
@keyframes fadeInDown {
0% {
-webkit-transform: matrix3d(.7, 0, 0, 0, 0, .7, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(.7, 0, 0, 0, 0, .7, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
opacity: 0
}
3.4% {
-webkit-transform: matrix3d(.795, 0, 0, 0, 0, .822, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(.795, 0, 0, 0, 0, .822, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
4.7% {
-webkit-transform: matrix3d(.835, 0, 0, 0, 0, .88, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(.835, 0, 0, 0, 0, .88, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
6.81% {
-webkit-transform: matrix3d(.898, 0, 0, 0, 0, .968, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(.898, 0, 0, 0, 0, .968, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
9.41% {
-webkit-transform: matrix3d(.965, 0, 0, 0, 0, 1.05, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(.965, 0, 0, 0, 0, 1.05, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
10.21% {
-webkit-transform: matrix3d(.982, 0, 0, 0, 0, 1.068, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(.982, 0, 0, 0, 0, 1.068, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
13.61% {
-webkit-transform: matrix3d(1.037, 0, 0, 0, 0, 1.1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1.037, 0, 0, 0, 0, 1.1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
14.11% {
-webkit-transform: matrix3d(1.042, 0, 0, 0, 0, 1.099, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1.042, 0, 0, 0, 0, 1.099, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
17.52% {
-webkit-transform: matrix3d(1.062, 0, 0, 0, 0, 1.072, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1.062, 0, 0, 0, 0, 1.072, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
18.72% {
-webkit-transform: matrix3d(1.064, 0, 0, 0, 0, 1.056, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1.064, 0, 0, 0, 0, 1.056, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
21.32% {
-webkit-transform: matrix3d(1.059, 0, 0, 0, 0, 1.021, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1.059, 0, 0, 0, 0, 1.021, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
24.32% {
-webkit-transform: matrix3d(1.045, 0, 0, 0, 0, .988, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1.045, 0, 0, 0, 0, .988, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
25.23% {
-webkit-transform: matrix3d(1.04, 0, 0, 0, 0, .981, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1.04, 0, 0, 0, 0, .981, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
29.03% {
-webkit-transform: matrix3d(1.019, 0, 0, 0, 0, .969, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1.019, 0, 0, 0, 0, .969, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
29.93% {
-webkit-transform: matrix3d(1.014, 0, 0, 0, 0, .97, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1.014, 0, 0, 0, 0, .97, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
35.54% {
-webkit-transform: matrix3d(.994, 0, 0, 0, 0, .989, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(.994, 0, 0, 0, 0, .989, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
36.74% {
-webkit-transform: matrix3d(.991, 0, 0, 0, 0, .994, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(.991, 0, 0, 0, 0, .994, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
41.04% {
-webkit-transform: matrix3d(.988, 0, 0, 0, 0, 1.007, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(.988, 0, 0, 0, 0, 1.007, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
44.44% {
-webkit-transform: matrix3d(.99, 0, 0, 0, 0, 1.01, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(.99, 0, 0, 0, 0, 1.01, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
52.15% {
-webkit-transform: matrix3d(.997, 0, 0, 0, 0, 1.002, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(.997, 0, 0, 0, 0, 1.002, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
opacity: 1
}
59.86% {
-webkit-transform: matrix3d(1.002, 0, 0, 0, 0, .997, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1.002, 0, 0, 0, 0, .997, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
63.26% {
-webkit-transform: matrix3d(1.002, 0, 0, 0, 0, .998, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1.002, 0, 0, 0, 0, .998, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
75.28% {
-webkit-transform: matrix3d(1, 0, 0, 0, 0, 1.001, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1, 0, 0, 0, 0, 1.001, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
85.49%,
90.69%,
100% {
-webkit-transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
100% {
opacity: 1
}
}
.dataBox {
top: 58px;
right: 32px;
position: absolute;
width: 250px;
background-color: rgba(0,0,0,.9);
border-radius: 15px;
animation: fadeInDown .3s;
.data_title {
display: flex;
justify-content: space-around;
font-size: 18px;
margin: 10px 0;
border-bottom: 1px solid grey;
}
.data_body {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
.data_box {
text-align: center;
width: 45px;
height: 40px;
margin: 5px;
line-height: 40px;
cursor: pointer;
vertical-align: center;
&:hover {
color: black;
background-color: grey;
}
}
}
}
}

View File

@@ -0,0 +1,28 @@
import React from "react";
import Flex from "@/components/Flex";
import less from './index.less';
/**
* 标题视图
* @constructor
*/
const TitleView = ({bgUrl, bgStyle = {}, title = "", extra}) => {
///////////////////////////////////////// 页面渲染
return <Flex justify={"space-between"} alignItems={"center"}>
<Flex
className={less.titleBg} justify={"center"} alignItems={"center"}
style={{...bgStyle, backgroundImage: `url(${bgUrl})`}}
>
<Flex>{title}</Flex>
</Flex>
{extra}
</Flex>
};
export default TitleView;

View File

@@ -0,0 +1,6 @@
.titleBg {
font-size: 18px;
background-size: contain;
background-repeat: no-repeat;
}

View File

@@ -0,0 +1,206 @@
import style from "./index.less";
import {ScrollBoard} from "@jiaminghi/data-view-react";
import {useEffect, useRef, useState} from "react";
import leftPageApi from "@/https/apis/leftPageApi";
import modalApi from "@/https/apis/modalApi";
const modalPage = ({onCloseUser}) => {
//用户弹框的轮播表
const [userVehicleList, setUserVehicleList] = useState([])
const [imgUrl, setImgUrl] = useState('')
const [detailVisible, setDetailVisible] = useState(false);
//获取车辆类型
const [illegalTypeList, setIllegalTypeList] = useState([])
//获取身份列表
const [idTypeList, setIdTypeList] = useState([])
//人员详情
const [configDetails, setConfigDetails] = useState({})
const [searchText, setSearchText] = useState('')
const [userListParams, setUserListParams] = useState({
vehicleTypeId: "", userType: ""
})
const scrollTable = useRef()
useEffect(() => {
getUserVehicleList()
}, [userListParams])
useEffect(() => {
getUserVehicleList()
carTypeList()
getIdTypeList()
}, [])
/**
* 获取人员车辆列表
*/
const getUserVehicleList = () => {
leftPageApi.userVehicleList({...userListParams, keyword: searchText}).then(res => {
if (res.code == 200) {
setUserVehicleList(res.data.map(it => {
return [it.name, it.no, it.sex == '1' ? '男' : '女', it.organName, it.vehicleCount, `<span style='display: none'>${it.id}</span>`]
}))
}
})
}
/**
* 获取车辆类型
* @param key
*/
const carTypeList = (key) => {
modalApi.carTypeList(key).then(res => {
if (res.code === 200) {
setIllegalTypeList(res.data.map(it => {
return {
name: it.name,
value: it.id,
}
}))
}
})
}
const getIdTypeList = () => {
leftPageApi.idTypeList().then(res => {
if (res.code == 200) {
setIdTypeList(res.data.map(it => {
return {
name: it.name,
value: it.id
}
}))
}
})
}
/**
* 点击轮播表获取详情事件
* @param userId
* @param vehicleTypeId
*/
const detailClick = (userId, vehicleTypeId) => {
leftPageApi.userVehicleDetails(vehicleTypeId, userId).then(res => {
if (res.code == 200) {
setImgUrl(res.data[0].imgs)
console.log(res.data)
setConfigDetails(res.data.map(it => {
return [it.name, it.type, it.plateNo, it.status, `<span style="display: none">${it.imgs}</span>`]
}))
}
})
setDetailVisible(true)
}
//用户轮播图
let userConfig = {
index: true,
headerBGC: 'rgba(47, 253, 239, 0.1)',
headerHeight: 65,
oddRowBGC: 'rgba(0, 0, 0, 0)',
evenRowBGC: 'rgba(47, 253, 239, 0.1)',
columnWidth: [60, 120, 140, 120, 200],
rowNum: 5,
header: [
"<span style='color: #BDFF79;'>姓名</span>",
"<span style='color: #BDFF79;'>ID</span>",
"<span style='color: #BDFF79;'>性别</span>",
"<span style='color: #BDFF79;'>部门</span>",
"<span style='color: #BDFF79;'>车辆</span>",
],
data: userVehicleList
}
//用户详情
let userDetailConfig = {
index: true,
headerBGC: 'rgba(47, 253, 239, 0.1)',
headerHeight: 60,
oddRowBGC: 'rgba(0, 0, 0, 0)',
evenRowBGC: 'rgba(47, 253, 239, 0.1)',
columnWidth: [150, 150, 150, 150],
rowNum: configDetails.length,
header: [
"<span style='color: #BDFF79;'>姓名</span>",
"<span style='color: #BDFF79;'>车辆类型</span>",
"<span style='color: #BDFF79;'>车牌号</span>",
"<span style='color: #BDFF79;'>车辆状态</span>",
],
data: configDetails
}
return (
<div>
{/* 人员列表弹框*/}
{
<div className={style.modal_box}>
<div className={style.modal_title}>
<span className={style.title_box}>人员车辆-查询</span>
<span style={{position: 'absolute', right: 10, cursor: 'pointer'}}
onClick={onCloseUser}>×</span>
</div>
<div className={style.modal_body}>
<select className={style.text} type="text" id="carType"
onChange={(e) => setUserListParams({...userListParams, vehicleTypeId: e.target.value})}
>
<option value="" style={{display: 'none'}}>车辆类型</option>
{illegalTypeList.map(it => {
return (
<option key={it.value} value={it.value}>{it.name}</option>
)
})}
</select>
<img src={require("@/assets/chain.png")} alt=""/>
<select className={style.text}
onChange={(e) => setUserListParams({...userListParams, userType: e.target.value})}>
<option value="" style={{display: 'none'}}>身份筛选</option>
<option key={''} value={''}>全部</option>
{idTypeList.map(it => {
return (<option key={it.value} value={it.value}>{it.name}</option>)
})}
</select>
<input className={style.text} placeholder="搜索" type="search"
onChange={e => setSearchText(e.target.value)}/>
<div className={style.search_btn}>
<img style={{position: "absolute", left: '25%', top: '25%', width: 25, height: 25}}
src={require("@/assets/searchWhite.png")} onClick={() => getUserVehicleList()}/>
</div>
</div>
<div style={{cursor: 'pointer', width: '100%',height:'100%',overflow:'auto'}}>
<ScrollBoard config={userConfig} style={{width: '100%', height: '300px'}}
onClick={(e) => detailClick(e.row[6]?.replace(/<[^>]*>/g, ""), userListParams.vehicleTypeId)}
/>
</div>
</div>}
{/* 详情弹框*/}
{
detailVisible &&
<div className={style.modal_box} style={{width: 769, height: 458}}>
<div className={style.modal_title}>
<span className={style.title_box}>人员车辆-详情</span>
<span style={{position: 'absolute', right: 10, cursor: 'pointer'}}
onClick={() => {
setDetailVisible(false)
setImgUrl('')
}}>×</span>
</div>
<div className={style.middleDetails}>
{imgUrl && <div className={style.pic}>
<img src={imgUrl} alt="" style={{width: '100%', height: '100%'}}/>
</div>}
</div>
<div className={style.board_box}>
<ScrollBoard config={userDetailConfig} style={{height: 170}} ref={scrollTable}
onClick={(e) => setImgUrl(e.row[5].replace(/<[^>]*>/g, ""))}/>
</div>
</div>
}
</div>
)
}
export default modalPage

View File

@@ -0,0 +1,103 @@
.modal_box {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-75%);
width: 769px;
height: 430px;
background: rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
border: 2px solid #10FDF2;
backdrop-filter: blur(10px);
.modal_title {
transform: translateY(5%);
height: 8%;
font-size: 18px;
font-weight: 500;
color: #FFFFFF;
line-height: 25px;
text-align: center;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
}
.userTable {
width: 100%;
height: 200px;
}
.modal_body {
display: flex;
justify-content: space-around;
align-items: center;
height: 22%;
.text {
height: 45px;
width: 25%;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
border-radius: 22px;
background-color: rgba(0, 0, 0, 0.1);
border: 1px solid #14FDF7;
font-size: 14px;
font-weight: 400;
color: #D5D8E2;
line-height: 20px;
text-align: center;
padding-left: 10px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
&:nth-child(1){
//transform: translateX(55px);
}
}
.title_box {
display: block;
font-size: 20px;
background-color: #10FDF2;
padding: 0 8px 5px 8px;
}
.search_btn {
position: absolute;
right: 15px;
width: 43px;
height: 43px;
border-radius: 50%;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
cursor: pointer;
background: #14FDF7;
}
option {
font-size: 14px;
// background: rgba(47, 253, 239, 0.1);
background-color: rgb(0, 52, 73);
color: #D5D8E2;
backdrop-filter: blur(10px);
border: 2px solid #24A7A4;
}
}
.middleDetails {
display: flex;
justify-content: space-around;
align-items: center;
height: 55%;
.pic {
width: 35%;
height: 205px;
border: 1px solid #14FDF7;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
}
}
.row-item {
height: 47px;
}
.board_box {
height: 200px;
cursor: pointer;
&:hover {
}
}
}

84
src/global.less Normal file
View File

@@ -0,0 +1,84 @@
* {
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
@primary-color: #14FDF7; // 全局主色
.ant-picker-panel-container {
background: black !important;
}
.ant-picker-header {
color: #14FDF7;
border-bottom: 1px solid #bfbfbf80;
}
.ant-picker-content th {
color: #14FDF7;
}
.ant-picker-content th:hover {
color: #1890ff;
}
// 其他月
.ant-picker-cell {
color: rgba(255, 255, 255, 0.25);
}
.ant-picker-cell:hover {
color: #000;
background-color: black;
.ant-picker-cell-inner {
background-color: #ffffff80 !important;
}
}
// 本月
.ant-picker-cell-in-view {
color: #14FDF7;
}
.ant-picker-cell-in-view:hover {
color: black;
}
.ant-picker-cell-in-view:hover {
.ant-picker-cell-inner {
background: #ffffff80 !important;
}
}
.ant-picker-panel .ant-picker-footer {
border-bottom: 1px solid #bfbfbf80;
border-top: 1px solid #bfbfbf80;
}
.ant-picker-panel {
border-bottom: 1px solid #bfbfbf80;
}
.ant-picker-header > button {
color: #fff;
}
.ant-picker-header > button:hover {
color: #14FDF7;
}
.ant-picker-range-arrow {
display: none;
background-color: #ffffff80 !important;
}
.ant-picker-input > input {
color: #FFFFFF;
}
.ant-picker-cell .ant-picker-cell-inner {
}

View File

@@ -0,0 +1,80 @@
import request from "@/https/request";
export default {
/**
* 获取车辆类型的数量
* @param data
*/
countType: async (data) => {
return request('/vehicle/manager/visual/management/illegal/count/type', {method: 'GET', data})
},
/**
* 获取本月违规数
*/
monthCount: async () => {
return request('/vehicle/manager/visual/management/illegal/current/month')
},
/**
* 获取上月违规数
*/
lastMonthCount: async () => {
return request('/vehicle/manager/visual/management/illegal/last/month')
},
/**
* 获取当日的违规数
*/
dayCount: async () => {
return request('/vehicle/manager/visual/management/illegal/today')
},
/**
* 获取昨日
*/
lastDay: async () => {
return request('/vehicle/manager/visual/management/illegal/yesterday')
},
/**
* 获取违规列表
* @return {Promise<any>}
*/
illegalList: async () => {
return request('/vehicle/manager/visual/management/illegal/page')
},
/**
* 获取违规总数
*/
illegalCount: async () => {
return request('/vehicle/manager/visual/management/illegal/count')
},
/**
* 获取人员车辆列表
*/
userVehicleList: async ({vehicleTypeId, userType, keyword}) => {
return request('/vehicle/manager/visual/management/illegal/user/vehicle', {
method: 'GET', params: {
vehicleTypeId,
userType,
keyword
}
})
},
/**
* 获取身份表
*/
idTypeList:async () => {
return request('/vehicle/manager/user/enum/page',{
method:'GET'
})
},
/**
* 获取人员车辆详情
*/
userVehicleDetails:async(vehicleTypeId, userId)=> {
return request('/vehicle/manager/visual/management/illegal/user/vehicle/detail',{
method:'GET',params:{
vehicleTypeId,
userId,
}
})
}
}

View File

@@ -0,0 +1,46 @@
import request from "@/https/request";
export default {
/**
* 获取学院违规统计
*/
getCollegeList: async (data) => {
return request("/vehicle/manager/visual/management/illegal/college/count", {method: 'GET', data});
},
/**
* 车辆类型列表
*/
carTypeList: async (data) => {
return request("/vehicle/manager/vehicle/type/page", {method: 'GET', data});
},
/**
* 违规类型列表
* @param vehicleTypeId
* @return {Promise<any>}
*/
illegalTypeList:async (vehicleTypeId) => {
return request('/vehicle/manager/score/rule/page',{method:'GET',params:{vehicleTypeId}})
},
/**
* 车辆违规列表
* @param startTime 开始时间
* @param endTime 结束时间
* @param no 学院编号
* @param vehicleTypeId 车辆类型
* @param ruleId 违规类型
* @param keyword 关键词
*/
illegalList: async ({vehicleTypeId, ruleId, keyword,startTime='',endTime='',no}) => {
return request("/vehicle/manager/visual/management/illegal/page", {
method: 'GET', params: {
vehicleTypeId,
ruleId,
keyword,
startTime,
endTime,
no,
}
});
},
}

View File

@@ -0,0 +1,23 @@
import request from "@/https/request";
export default {
/**
* 学院违规统计
*/
collegeCount:async (data) => {
return request('/vehicle/manager/visual/management/illegal/college/count', {params: {dateTime: data}})
},
/**
* 近七天的违规类型统计
* @return {Promise<any>}
*/
sevenType:async () => {
return request('/vehicle/manager/visual/management/illegal/in/seven/type')
},
/**
* 近七天每天的违规量
*/
sevenCount:async () => {
return request('/vehicle/manager/visual/management/illegal/in/seven/days')
}
}

99
src/https/request.js Normal file
View File

@@ -0,0 +1,99 @@
/**
* request 网络请求工具
* 更详细的 api 文档: https://github.com/umijs/umi-request
*/
import {extend} from 'umi-request';
import Common from "@/utils/Common";
const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。',
};
/**
* 保存用户登录信息
*/
export const saveLoginInfo = (data) => {
sessionStorage.setItem('token', data.token);
sessionStorage.setItem('nickName', data.user.nickName);
};
/**
* 异常处理程序
*/
const errorHandler = error => {
const {response} = error;
let res = {
code: -1,
message: '请求初始化时出错或者服务器没有响应返回'
};
if (!Common.isEmpty(response)) {
res.code = response.status;
res.message = codeMessage[response.status] || response.statusText;
}
return res;
};
/**
* 配置request请求时的默认参数
*/
const request = extend({
errorHandler,
// 默认错误处理
credentials: 'include', // 默认请求是否带上cookie
requestType: "json", // 表单提交post请求
});
request.use((async (ctx, next) => {
const {options} = ctx.req;
options.headers = {...options.headers, token: sessionStorage.getItem('token')};
// 存在分页器的时候
if (options.params?.pagination) {
options.params.length = options.params.pagination.pageSize;
options.params.start = options.params?.length * (options.params.pagination.current - 1);
delete options.params['pagination'];
}
if (options.data?.pagination) {
options.data.length = options.data.pagination.pageSize;
options.data.start = options.data.length * (options.data.pagination.current - 1);
delete options.data['pagination'];
}
// equals 处理
if (options.params && options.params.equals) {
let ids = [];
let equalsObj = options.params.equals;
Object.keys(equalsObj).map(key => {
if (!Common.isEmpty(equalsObj[key])) {
ids.push(key + "=" + equalsObj[key]);
}
});
options.params.equals = ids.join(",");
}
// 增加通用参数
await next();
}));
export default request;

179
src/pages/document.ejs Normal file
View File

@@ -0,0 +1,179 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0">
<script crossorigin src="./react.production.min.js"></script>
<script crossorigin src="./react-dom.production.min.js"></script>
<script crossorigin src="./charts.min.js"></script>
</head>
<body>
<noscript>Out-of-the-box mid-stage front/design solution!</noscript>
<div id="root">
<style>html,
body,
#root {
height: 100%;
margin: 0;
padding: 0;
}
.page-loading-warp {
padding: 24px 98px;
display: flex;
justify-content: center;
align-items: center;
}
.ant-spin {
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
list-style: none;
-webkit-font-feature-settings: 'tnum';
font-feature-settings: 'tnum';
position: absolute;
display: none;
color: #1890ff;
text-align: center;
vertical-align: middle;
opacity: 0;
-webkit-transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
-webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
}
.ant-spin-spinning {
position: static;
display: inline-block;
opacity: 1;
}
.ant-spin-dot {
position: relative;
display: inline-block;
font-size: 20px;
width: 20px;
height: 20px;
}
.ant-spin-dot-item {
position: absolute;
display: block;
width: 9px;
height: 9px;
background-color: #1296db;
border-radius: 100%;
-webkit-transform: scale(0.75);
-ms-transform: scale(0.75);
transform: scale(0.75);
-webkit-transform-origin: 50% 50%;
-ms-transform-origin: 50% 50%;
transform-origin: 50% 50%;
opacity: 0.3;
-webkit-animation: antSpinMove 1s infinite linear alternate;
animation: antSpinMove 1s infinite linear alternate;
}
.ant-spin-dot-item:nth-child(1) {
top: 0;
left: 0;
}
.ant-spin-dot-item:nth-child(2) {
top: 0;
right: 0;
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
}
.ant-spin-dot-item:nth-child(3) {
right: 0;
bottom: 0;
-webkit-animation-delay: 0.8s;
animation-delay: 0.8s;
}
.ant-spin-dot-item:nth-child(4) {
bottom: 0;
left: 0;
-webkit-animation-delay: 1.2s;
animation-delay: 1.2s;
}
.ant-spin-dot-spin {
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-animation: antRotate 1.2s infinite linear;
animation: antRotate 1.2s infinite linear;
}
.ant-spin-lg .ant-spin-dot {
font-size: 32px;
width: 32px;
height: 32px;
}
.ant-spin-lg .ant-spin-dot i {
width: 14px;
height: 14px;
}
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.ant-spin-blur {
background: #fff;
opacity: 0.6;
}
}
@-webkit-keyframes antSpinMove {
to {
opacity: 1;
}
}
@keyframes antSpinMove {
to {
opacity: 1;
}
}
@-webkit-keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
@keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}</style>
<div
style="display: flex;justify-content: center;align-items: center;flex-direction: column;min-height: 420px;height: 100%;">
<div class="page-loading-warp">
<div class="ant-spin ant-spin-lg ant-spin-spinning">
<span class="ant-spin-dot ant-spin-dot-spin">
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
</span>
</div>
</div>
</div>
</div>
</body>
</html>

36
src/pages/index.jsx Normal file
View File

@@ -0,0 +1,36 @@
import React, {useRef, useState} from "react";
import less from './index.less'
import LeftPage from '../components/LeftPage'
import RightPage from '../components/RightPage'
import MiddlePage from '../components/MiddlePage'
import {BorderBox6} from "@jiaminghi/data-view-react";
import CarModalPage from "@/components/CarModalPage";
import ModalPage from "@/components/UserModalPage";
import Flex from "@/components/Flex";
const width = 2120, height = 1295;
const bgUrl = window.sessionStorage.getItem('core_3d_map_bg');
export default () => {
const carModalPageRef = useRef();
///////////////////////////////////////// 页面渲染
return <Flex className={less.body} style={{backgroundImage: `url(${bgUrl})`}}>
<LeftPage/>
<Flex itemGrow={1} style={{width: 0}}>
</Flex>
<Flex>
<RightPage/>
</Flex>
</Flex>
}

129
src/pages/index.less Normal file
View File

@@ -0,0 +1,129 @@
.body {
width: 100vw;
height: 100vh;
background-size: 100% 100%;
background-repeat: no-repeat;
}
.container {
padding: 5px 15px;
//background-image: url("../assets/map.png") ;
background-color: black;
}
.containerBody {
display: flex;
flex-direction: row;
justify-content: center;
width: 100%;
height: 88%;
}
.left_box {
width: 20%;
//height: 95%;
//min-width: 900px;
//background-color: aqua;
}
.middle_box {
width: 60%;
//background-color: saddlebrown;
}
.right_box {
width: 20%;
//background-color: greenyellow;
}
.modal_box {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -75%);
width: 769px;
height: 430px;
background: rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
border: 2px solid #10FDF2;
backdrop-filter: blur(10px);
.modal_title {
transform: translateY(5%);
height: 8%;
font-size: 18px;
font-weight: 500;
color: #FFFFFF;
line-height: 25px;
text-align: center;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
}
.modal_body {
display: flex;
justify-content: space-around;
align-items: center;
height: 22%;
.text {
height: 45px;
width: 25%;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
border-radius: 22px;
background-color: rgba(0, 0, 0, 0.1);
border: 1px solid #14FDF7;
font-size: 14px;
font-weight: 400;
color: #D5D8E2;
line-height: 20px;
text-align: center;
padding-left: 10px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
&:nth-child(1) {
//transform: translateX(55px);
}
}
.title_box {
display: block;
font-size: 20px;
background-color: #10FDF2;
padding: 0 8px 5px 8px;
}
.search_btn {
position: absolute;
right: 15px;
width: 43px;
height: 43px;
border-radius: 50%;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
cursor: pointer;
background: #14FDF7;
}
option {
font-size: 14px;
// background: rgba(47, 253, 239, 0.1);
background-color: rgb(0, 52, 73);
color: #D5D8E2;
backdrop-filter: blur(10px);
border: 2px solid #24A7A4;
}
}
.middleDetails {
display: flex;
justify-content: space-around;
align-items: center;
height: 69%;
.pic {
width: 45%;
height: 200px;
border: 1px solid #14FDF7;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.25);
}
}
}

34
src/utils/AppCommon.js Normal file
View File

@@ -0,0 +1,34 @@
import request from '@/https/request';
import AppHelper from '@/utils/AppHelper';
import {stringify} from 'qs';
export default {
/**
* 下载文件
* @param url
* @param params
*/
downloadFile: (url, params) => {
return request(url, {method: 'POST', data: {...params}}).then(res => {
if (res.code === 200) {
window.location.href = res.data;
} else {
AppHelper.showRespError(res);
}
});
},
/**
* 直接下载文件
* @param url
* @param params
*/
downloadByWindow: (url, params) => {
let query = {token: sessionStorage.getItem('token'), ...params};
window.location.href = url + '?' + stringify(query);
},
};

70
src/utils/AppHelper.js Normal file
View File

@@ -0,0 +1,70 @@
import {message} from "antd";
import {history} from 'umi';
import Common from "@/utils/Common";
/**
* 显示提示消息
* @param msg
* @param type
* @param callback
*/
const showToast = (msg, type = 'success', callback) => {
message[type](msg, 3, callback);
};
/**
* 显示网络请求错误
* @param res
*/
const showRespError = (res) => {
if (res && res.code !== 200) {
message.destroy();
message['error'](res.message, 3, () => {
if (res.code == 110) {
routerPush('/login');
}
});
}
};
/**
* 显示加载框
* @param msg
*/
const showLoading = (msg = '数据处理中,请稍等') => {
message.loading(msg, 0);
};
/**
* 隐藏message组件
*/
const hideMessage = () => {
message.destroy();
};
/**
* 返回上一级
*/
const routerBack = () => {
history.goBack();
};
/**
* 跳转指定目录
* @param pathname
* @param query
*/
const routerPush = (pathname, query) => {
if (Common.isEmpty(query)) {
history.push(pathname);
} else {
history.push({pathname, query: {...query}});
}
};
export default {
showToast, showRespError, showLoading,
hideMessage, routerBack, routerPush
}

140
src/utils/Common.js Normal file
View File

@@ -0,0 +1,140 @@
/**
* 获取Url中的参数
*/
const getParamFormUrl = (key, host) => {
let arr;
let reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)');
try {
let testHost = window.location.href;
if (host) {
testHost = host;
}
if ((arr = testHost.split('?')[1].match(reg))) {
return decodeURI(arr[2]);
}
} catch (e) {
}
return '';
};
/**
* 判断是否开始
* @param value
* @param start
*/
const startWidth = (value, start = '') => {
return initValue(value, '').substr(0, start.length) === start;
};
/**
* 是否为空
* @param value
* @returns {boolean}
*/
const isEmpty = (value) => {
return value == undefined || value == null || value == '';
};
/**
* 设置默认值
* @param value
* @param def
*/
const initValue = (value, def) => {
return isEmpty(value) ? def : value;
};
/**
* 转换为json
* @param value
* @param def = {}
*/
const parseJSON = (value, def) => {
let obj;
try {
obj = JSON.parse(value);
} catch (e) {
}
if (!obj) {
obj = def;
}
return obj;
};
/**
* 将array中的某个key用sep连接起来
* @param array
* @param key
* @param sep
*/
const arrayKeys = (array = [], key = 'id', sep = ',') => {
let result = '';
array.forEach(item => {
if (typeof item == 'object') {
result = result + item[key];
} else {
result = result + item;
}
result = result + sep;
});
result = result.substring(0, result.length - sep.length);
return result;
};
/**
* 分割对象
* @param obj
* @param sep
*/
const splitObj = (obj, sep = ',') => {
if (obj) {
return obj.split(sep);
}
return [];
};
/**
* 字符串长度
* @param val
* @returns {number}
*/
const length = (val = '') => {
return isEmpty(val) ? 0 : val.length;
};
const arraySet = (array = [], key) => {
let obj = {};
array.forEach(item => {
obj[item[key]] = item;
});
return Object.keys(obj).map(k => (obj[k]));
};
/**
* 上传图片
*/
const uploadFile = (file) => {
return new Promise((resolve, reject) => {
let fileData = new FormData();
fileData.append('file', file);
let request = new XMLHttpRequest();
request.open("POST", "/tools/file/whole_upload");
request.onreadystatechange = ev => {
if (ev.currentTarget.readyState === 4) {
let res = JSON.parse(ev.currentTarget.responseText);
resolve(res);
}
};
request.send(fileData);
});
};
export default {
getParamFormUrl, isEmpty, initValue, startWidth, arraySet,
parseJSON, arrayKeys, splitObj, length, uploadFile
}