初始化提交
|
|
@ -0,0 +1 @@
|
||||||
|
PUBLIC_URL=/xyt_res_center
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
GENERATE_SOURCEMAP=false
|
||||||
|
# PUBLIC_URL=http://local.gunshiiot.com:19080/resCenter
|
||||||
|
PUBLIC_URL=/xyt/resCenter
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
yarn.lock
|
||||||
|
build.7z
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
# Getting Started with Create React App
|
||||||
|
|
||||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `npm start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.\
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||||
|
|
||||||
|
The page will reload if you make edits.\
|
||||||
|
You will also see any lint errors in the console.
|
||||||
|
|
||||||
|
### `npm test`
|
||||||
|
|
||||||
|
Launches the test runner in the interactive watch mode.\
|
||||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||||
|
|
||||||
|
### `npm run build`
|
||||||
|
|
||||||
|
Builds the app for production to the `build` folder.\
|
||||||
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.\
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||||
|
|
||||||
|
### `npm run eject`
|
||||||
|
|
||||||
|
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||||
|
|
||||||
|
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||||
|
|
||||||
|
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||||
|
|
||||||
|
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
const CracoLessPlugin = require('craco-less');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
plugin: CracoLessPlugin,
|
||||||
|
options: {
|
||||||
|
lessLoaderOptions: {
|
||||||
|
lessOptions: {
|
||||||
|
modifyVars: {
|
||||||
|
'@primary-color': '#3B7CFF',
|
||||||
|
'@error-color': '#F55E55',
|
||||||
|
'@text-color': '#3B4859',
|
||||||
|
'@menu-dark-bg': '#3773C5',
|
||||||
|
'@layout-body-background': '#eeeeee',
|
||||||
|
'@form-vertical-label-padding': '0 0 10px',
|
||||||
|
'@normal-color': '#EFF1F5',
|
||||||
|
'@label-color': '#3B4859',
|
||||||
|
'@card-background': '#fbfbfb',
|
||||||
|
'@heading-color': '#3B4859',
|
||||||
|
},
|
||||||
|
javascriptEnabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"name": "zhzmkz-web",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "^4.7.0",
|
||||||
|
"@craco/craco": "^7.0.0-alpha.7",
|
||||||
|
"@react-hook/resize-observer": "^1.2.6",
|
||||||
|
"@rematch/core": "^2.2.0",
|
||||||
|
"@types/geojson": "^7946.0.8",
|
||||||
|
"@types/node": "^16.11.45",
|
||||||
|
"@types/react": "^18.0.15",
|
||||||
|
"@types/react-dom": "^18.0.6",
|
||||||
|
"antd": "^4.21.7",
|
||||||
|
"axios": "^0.27.2",
|
||||||
|
"craco-less": "2.1.0-alpha.0",
|
||||||
|
"echarts": "^5.3.3",
|
||||||
|
"echarts-for-react": "^3.0.2",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-redux": "^8.0.2",
|
||||||
|
"react-router": "^6.3.0",
|
||||||
|
"react-router-dom": "^6.3.0",
|
||||||
|
"react-scripts": "5.0.1",
|
||||||
|
"redux": "^4.2.0",
|
||||||
|
"typescript": "^4.7.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "craco start",
|
||||||
|
"build": "craco build"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 689 B |
|
After Width: | Height: | Size: 240 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 858 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 443 KiB |
|
|
@ -0,0 +1,136 @@
|
||||||
|
{
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"序号": "1",
|
||||||
|
"站名": "水位雨量站",
|
||||||
|
"管理单位": "",
|
||||||
|
"RTU编码": "0020230509",
|
||||||
|
"经度": "112.107041",
|
||||||
|
"纬度": "30.961038",
|
||||||
|
"基础准高程": "0",
|
||||||
|
"修正值": "-0.01",
|
||||||
|
"站类": "水库水文站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期#": "2023-05-15"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"序号": "2",
|
||||||
|
"站名": "输水管流量站",
|
||||||
|
"管理单位": "",
|
||||||
|
"RTU编码": "0020230430",
|
||||||
|
"经度": "112.107041",
|
||||||
|
"纬度": "30.961038",
|
||||||
|
"基础准高程": "",
|
||||||
|
"修正值": "",
|
||||||
|
"站类": "流量站",
|
||||||
|
"归属协议": "水库供水",
|
||||||
|
"创建日期#": "2023-05-05"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"序号": "3",
|
||||||
|
"站名": "供水管流量站",
|
||||||
|
"管理单位": "",
|
||||||
|
"RTU编码": "0020230429",
|
||||||
|
"经度": "112.107041",
|
||||||
|
"纬度": "30.961038",
|
||||||
|
"基础准高程": "",
|
||||||
|
"修正值": "",
|
||||||
|
"站类": "流量站",
|
||||||
|
"归属协议": "水库供水",
|
||||||
|
"创建日期#": "2023-05-04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"序号": "4",
|
||||||
|
"站名": "生态流量站",
|
||||||
|
"管理单位": "",
|
||||||
|
"RTU编码": "4220230428",
|
||||||
|
"经度": "112.107041",
|
||||||
|
"纬度": "30.961038",
|
||||||
|
"基础准高程": "",
|
||||||
|
"修正值": "",
|
||||||
|
"站类": "流量站",
|
||||||
|
"归属协议": "水库供水",
|
||||||
|
"创建日期#": "2023-04-28"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"序号": "5",
|
||||||
|
"站名": "SY01",
|
||||||
|
"管理单位": "",
|
||||||
|
"RTU编码": "0020230426",
|
||||||
|
"经度": "112.107041",
|
||||||
|
"纬度": "30.961038",
|
||||||
|
"基础准高程": "",
|
||||||
|
"修正值": "",
|
||||||
|
"站类": "渗压站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期#": "2023-04-26"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"序号": "6",
|
||||||
|
"站名": "SY02",
|
||||||
|
"管理单位": "",
|
||||||
|
"RTU编码": "000000036",
|
||||||
|
"经度": "112.107041",
|
||||||
|
"纬度": "30.961038",
|
||||||
|
"基础准高程": "",
|
||||||
|
"修正值": "",
|
||||||
|
"站类": "渗压站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期#": "2023-04-17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"序号": "7",
|
||||||
|
"站名": "SY03",
|
||||||
|
"管理单位": "",
|
||||||
|
"RTU编码": "0020230414",
|
||||||
|
"经度": "112.107041",
|
||||||
|
"纬度": "30.961038",
|
||||||
|
"基础准高程": "0",
|
||||||
|
"修正值": "",
|
||||||
|
"站类": "渗压站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期#": "2023-04-14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"序号": "8",
|
||||||
|
"站名": "SY04",
|
||||||
|
"管理单位": "",
|
||||||
|
"RTU编码": "4208820200",
|
||||||
|
"经度": "112.107041",
|
||||||
|
"纬度": "30.961038",
|
||||||
|
"基础准高程": "108.487",
|
||||||
|
"修正值": "0.06",
|
||||||
|
"站类": "渗压站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期#": "2023-04-04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"序号": "9",
|
||||||
|
"站名": "SY05",
|
||||||
|
"管理单位": "",
|
||||||
|
"RTU编码": "0020230325",
|
||||||
|
"经度": "112.107041",
|
||||||
|
"纬度": "30.961038",
|
||||||
|
"基础准高程": "0",
|
||||||
|
"修正值": "",
|
||||||
|
"站类": "渗压站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期#": "2023-03-25"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"序号": "10",
|
||||||
|
"站名": "SY06",
|
||||||
|
"管理单位": "",
|
||||||
|
"RTU编码": "4202023102",
|
||||||
|
"经度": "112.107041",
|
||||||
|
"纬度": "30.961038",
|
||||||
|
"基础准高程": "108.42",
|
||||||
|
"修正值": "",
|
||||||
|
"站类": "渗压站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期#": "2023-03-14"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"大坝代码": "HP0024208220000169",
|
||||||
|
"大坝名称": "大坝",
|
||||||
|
"是否主坝": "是",
|
||||||
|
"大坝所在位置": "乘马岗镇",
|
||||||
|
"最大坝高": "66.50",
|
||||||
|
"坝顶长度": "630.00",
|
||||||
|
"坝顶宽度": "8.00",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
}
|
||||||
|
|
||||||
|
],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"records":[
|
||||||
|
{"序号":"1","部门编码":"A01","部门名称":"办公室","上级部门":"","排序号":"1","备注":"","创建日期":"2023-08-30"},
|
||||||
|
{"序号":"2","部门编码":"A02","部门名称":"工程科","上级部门":"","排序号":"2","备注":"","创建日期":"2023-08-30"},
|
||||||
|
{"序号":"3","部门编码":"A03","部门名称":"养护科","上级部门":"","排序号":"3","备注":"","创建日期":"2023-08-29"}
|
||||||
|
],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
{
|
||||||
|
"records": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"服务类型": "数据资源",
|
||||||
|
"serviceName": "流量",
|
||||||
|
"describe": "流量数据",
|
||||||
|
"serviceAddr": "121.40.63.15",
|
||||||
|
"port": "7889",
|
||||||
|
"route": "",
|
||||||
|
"registerTime": "2023-05-24 12:00:29",
|
||||||
|
"provider": "江苏德高物联技术有限公司",
|
||||||
|
"stNum": 1,
|
||||||
|
"contacts": "",
|
||||||
|
"telephone": "",
|
||||||
|
"target": "DB_ZHSK.ST_FLOW_R",
|
||||||
|
"enable": "1",
|
||||||
|
"服务状态": "1",
|
||||||
|
"maxTime": "2023-10-09 15:00:00",
|
||||||
|
"countVo": {
|
||||||
|
"allCount": 6495,
|
||||||
|
"todayCount": 16,
|
||||||
|
"weekCount": 16,
|
||||||
|
"monthCount": 208,
|
||||||
|
"yearCount": 6495
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"服务类型": "数据资源",
|
||||||
|
"serviceName": "气象预报服务",
|
||||||
|
"describe": "气象预报服务",
|
||||||
|
"serviceAddr": "10.42.160.53",
|
||||||
|
"port": "9527",
|
||||||
|
"route": "",
|
||||||
|
"registerTime": "2023-05-24 12:00:28",
|
||||||
|
"provider": "襄阳气象局",
|
||||||
|
"stNum": 1,
|
||||||
|
"contacts": "",
|
||||||
|
"telephone": "",
|
||||||
|
"target": "DB_ZHSK.WEATHER_DATA",
|
||||||
|
"enable": "1",
|
||||||
|
"服务状态": "1",
|
||||||
|
"maxTime": "2023-10-08 20:00:00",
|
||||||
|
"countVo": {
|
||||||
|
"allCount": 394766,
|
||||||
|
"todayCount": 0,
|
||||||
|
"weekCount": 0,
|
||||||
|
"monthCount": 15190,
|
||||||
|
"yearCount": 319968
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"服务类型": "数据资源",
|
||||||
|
"serviceName": "气象实况服务",
|
||||||
|
"describe": "气象实况服务",
|
||||||
|
"serviceAddr": "58.19.239.227",
|
||||||
|
"port": "10088",
|
||||||
|
"route": "",
|
||||||
|
"registerTime": "2023-05-24 12:00:26",
|
||||||
|
"provider": "襄阳气象局",
|
||||||
|
"stNum": 176,
|
||||||
|
"contacts": "",
|
||||||
|
"telephone": "",
|
||||||
|
"target": "DB_ZHSK.WEATHER_XY_SK",
|
||||||
|
"enable": "1",
|
||||||
|
"服务状态": "1",
|
||||||
|
"maxTime": "2023-10-09 15:30:00",
|
||||||
|
"countVo": {
|
||||||
|
"allCount": 3719924,
|
||||||
|
"todayCount": 16389,
|
||||||
|
"weekCount": 16389,
|
||||||
|
"monthCount": 216343,
|
||||||
|
"yearCount": 3719924
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"服务类型": "数据资源",
|
||||||
|
"serviceName": "水质服务",
|
||||||
|
"describe": "水质服务",
|
||||||
|
"serviceAddr": "218.200.85.79",
|
||||||
|
"port": "4505",
|
||||||
|
"route": "",
|
||||||
|
"registerTime": "2023-05-24 12:00:00",
|
||||||
|
"provider": "力合科技(湖南)股份有限公司",
|
||||||
|
"stNum": 1,
|
||||||
|
"contacts": "李晶晶",
|
||||||
|
"telephone": "15827237521",
|
||||||
|
"target": "DB_ZHSK.HYD_RI_WQAMD_W",
|
||||||
|
"enable": "1",
|
||||||
|
"服务状态": "1",
|
||||||
|
"maxTime": "2023-10-09 15:00:00",
|
||||||
|
"countVo": {
|
||||||
|
"allCount": 18271,
|
||||||
|
"todayCount": 15,
|
||||||
|
"weekCount": 15,
|
||||||
|
"monthCount": 207,
|
||||||
|
"yearCount": 3634
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"服务类型": "数据资源",
|
||||||
|
"serviceName": "水文服务",
|
||||||
|
"describe": "水文服务",
|
||||||
|
"serviceAddr": "47.112.66.254",
|
||||||
|
"port": "80",
|
||||||
|
"route": "",
|
||||||
|
"registerTime": "2023-05-24 12:00:00",
|
||||||
|
"provider": "鲧石",
|
||||||
|
"stNum": 215,
|
||||||
|
"contacts": "曾杰",
|
||||||
|
"telephone": "15697168180",
|
||||||
|
"target": "DB_ZHSK.REL_ST_STTP,DB_ZHSK.ST_RIVER_R",
|
||||||
|
"enable": "1",
|
||||||
|
"服务状态": "1",
|
||||||
|
"maxTime": "2023-10-09 15:00:00",
|
||||||
|
"countVo": {
|
||||||
|
"allCount": 3866780,
|
||||||
|
"todayCount": 1504,
|
||||||
|
"weekCount": 1504,
|
||||||
|
"monthCount": 19726,
|
||||||
|
"yearCount": 651781
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"服务类型": "数据资源",
|
||||||
|
"serviceName": "水库服务",
|
||||||
|
"describe": "水库服务",
|
||||||
|
"serviceAddr": "10.42.160.200",
|
||||||
|
"port": "1433",
|
||||||
|
"route": "",
|
||||||
|
"registerTime": "2023-05-24 12:00:00",
|
||||||
|
"provider": "鲧石",
|
||||||
|
"stNum": 372,
|
||||||
|
"contacts": "曾杰",
|
||||||
|
"telephone": "15697168180",
|
||||||
|
"target": "DB_ZHSK.REL_ST_STTP,DB_ZHSK.ST_RSVR_R",
|
||||||
|
"enable": "1",
|
||||||
|
"服务状态": "1",
|
||||||
|
"maxTime": "2023-10-09 16:00:00",
|
||||||
|
"countVo": {
|
||||||
|
"allCount": 8813744,
|
||||||
|
"todayCount": 10226,
|
||||||
|
"weekCount": 10226,
|
||||||
|
"monthCount": 111975,
|
||||||
|
"yearCount": 4113346
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 8,
|
||||||
|
"size": 15,
|
||||||
|
"current": 1,
|
||||||
|
"orders": [],
|
||||||
|
"optimizeCountSql": true,
|
||||||
|
"searchCount": true,
|
||||||
|
"countId": null,
|
||||||
|
"maxLimit": null,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
{
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"水电站代码": "HP0034208020000016",
|
||||||
|
"水电站名称": "水电站1",
|
||||||
|
"水电站类型": "闸坝式水电站",
|
||||||
|
"装机容量": "3000.0",
|
||||||
|
"多年平均发电量": "7.00",
|
||||||
|
"地点": "乘马岗镇",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"水电站代码": "HP0034208020000020",
|
||||||
|
"水电站名称": "水电站2",
|
||||||
|
"水电站类型": "闸坝式水电站",
|
||||||
|
"装机容量": "3000.0",
|
||||||
|
"多年平均发电量": "7.00",
|
||||||
|
"地点": "乘马岗镇",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"水电站代码": "HP0034208020000035",
|
||||||
|
"水电站名称": "水电站3",
|
||||||
|
"水电站类型": "闸坝式水电站",
|
||||||
|
"装机容量": "3000.0",
|
||||||
|
"多年平均发电量": "7.00",
|
||||||
|
"地点": "乘马岗镇",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"水电站代码": "HP003420802000004X",
|
||||||
|
"水电站名称": "水电站4",
|
||||||
|
"水电站类型": "闸坝式水电站",
|
||||||
|
"装机容量": "3000.0",
|
||||||
|
"多年平均发电量": "7.00",
|
||||||
|
"地点": "乘马岗镇",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"水电站代码": "HP0034205820000056",
|
||||||
|
"水电站名称": "水电站5",
|
||||||
|
"水电站类型": "闸坝式水电站",
|
||||||
|
"装机容量": "3000.0",
|
||||||
|
"多年平均发电量": "7.00",
|
||||||
|
"地点": "乘马岗镇",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"水电站代码": "HP0034208220000060",
|
||||||
|
"水电站名称": "水电站6",
|
||||||
|
"水电站类型": "闸坝式水电站",
|
||||||
|
"装机容量": "3000.0",
|
||||||
|
"多年平均发电量": "7.00",
|
||||||
|
"地点": "乘马岗镇",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"水电站代码": "HP0034208220000075",
|
||||||
|
"水电站名称": "水电站7",
|
||||||
|
"水电站类型": "闸坝式水电站",
|
||||||
|
"装机容量": "3000.0",
|
||||||
|
"多年平均发电量": "7.00",
|
||||||
|
"地点": "乘马岗镇",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"水电站代码": "HP0034208810000085",
|
||||||
|
"水电站名称": "水电站8",
|
||||||
|
"水电站类型": "闸坝式水电站",
|
||||||
|
"装机容量": "3000.0",
|
||||||
|
"多年平均发电量": "7.00",
|
||||||
|
"地点": "乘马岗镇",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"水电站代码": "HP0034208020000092",
|
||||||
|
"水电站名称": "水电站9",
|
||||||
|
"水电站类型": "闸坝式水电站",
|
||||||
|
"装机容量": "3000.0",
|
||||||
|
"多年平均发电量": "7.00",
|
||||||
|
"地点": "乘马岗镇",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"水电站代码": "HP0034208040000107",
|
||||||
|
"水电站名称": "水电站10",
|
||||||
|
"水电站类型": "闸坝式水电站",
|
||||||
|
"装机容量": "3000.0",
|
||||||
|
"多年平均发电量": "7.00",
|
||||||
|
"地点": "乘马岗镇",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"records": [{"序号":"1","管理单位代码":"ENG100039","管理单位名称":"","所属行政区划名称":"乘马岗镇","经度":"112.107041","纬度":"30.961038","管理用房面积(m³)":"190","人员情况(人)":"8","备注":""}],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"records": [{"序号":"1","量水设施代码":"HP0024208020000063","量水设施名称":"量水堰1#","设施类别":"量水槽","经度":"114. 989405","纬度":"31. 36019","所在位置":"溢洪道下游","喉道尺寸(m*m)":"1.5*1.2","设计流量(m³/s)":"2","实达流量(m³/s)":"2","创建日期":"2023-08-30"}],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"records":[{"水库代码":"SK2022083001","水库名称":"小玉潭水库","测站编码":"0020230509","行政区划":"乘马岗镇","经度":"114. 989405","纬度":"31.36019","水库规模":"小(1)型","集雨面积(km²)":"5.61","总库容(万m³)":"117.01","创建日期":"2023-08-30"}],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"records": [{"序号":"1","河流代码":"RL0024208220000169","河流名称":"陈店河","河流长度(km)":"10 200","最大水深(m)":"200","经度":"114. 989405","纬度":"31. 36019","河源所在位置":"陈店镇","河流流域面积(km²)":"27.00","承雨面积(km²)":"5.61","创建日期":"2023-08- 30"}],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"类型名称": "数据资源",
|
||||||
|
"类型描述": "数据资源类型"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
{
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"站名": "站名1",
|
||||||
|
"管理单位": "小玉潭",
|
||||||
|
"RTU编码": "0020230509",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"基础准高程": "0",
|
||||||
|
"修正值": "-0.01",
|
||||||
|
"站类": "雨量站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"站名": "站名4",
|
||||||
|
"管理单位": "小玉潭",
|
||||||
|
"RTU编码": "0020230509",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"基础准高程": "0",
|
||||||
|
"修正值": "-0.01",
|
||||||
|
"站类": "水库水文站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"站名": "站名5",
|
||||||
|
"管理单位": "小玉潭",
|
||||||
|
"RTU编码": "0020230509",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"基础准高程": "0",
|
||||||
|
"修正值": "-0.01",
|
||||||
|
"站类": "流量站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"站名": "站名6",
|
||||||
|
"管理单位": "小玉潭",
|
||||||
|
"RTU编码": "0020230509",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"基础准高程": "0",
|
||||||
|
"修正值": "-0.01",
|
||||||
|
"站类": "流量站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"站名": "站名7",
|
||||||
|
"管理单位": "小玉潭",
|
||||||
|
"RTU编码": "0020230509",
|
||||||
|
"经度": "114.989405",
|
||||||
|
"纬度": "31.36019",
|
||||||
|
"基础准高程": "0",
|
||||||
|
"修正值": "-0.01",
|
||||||
|
"站类": "流量站",
|
||||||
|
"归属协议": "水资源协议",
|
||||||
|
"创建日期": "2023-08-30"
|
||||||
|
}
|
||||||
|
|
||||||
|
],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"records":
|
||||||
|
[{"序号":"1","测站编码":"0020230509","站点名称":"站名1","创建时间":"2023-08-30"},{"序号":"2","测站编码":"0020230430","站点名称":"站名2","创建时间":"2023- 08-30"},{"序号":"3","测站编码":"0020230429","站点名称":"站名3","创建时间":"2023-08-29"},{"序号":"4","测站编码":"4220230428","站点名称":"站名4","创建时间":"2023-08-29"},{"序号":"5","测站编码":"0020230426","站点名称":"站名5","创建时间":"2023-08-29"},{"序号":"6","测站编码":"0000000036","站点名称":"站名6","创建时间":"2023-08-26"},{"序号":"7","测站编码":"0020230414","站点名称":"站名7","创建时间":"2023-08-24"},{"序号":"8","测站编码":"4208820200","站点名称":"站名8","创建时间":"2023-08-18"},{"序号":"9","测站编码":"0020230325","站点名称":"站名9","创建时间":"2023-08-15"},{"序号":"10","测站编码":"4202023102","站点名称":"站名10","创建时间":"2023-08-12"}]
|
||||||
|
,
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"records": [{"序号":"1","用户帐号":"wqf","用户姓名":"王群飞","手机号码":"1393123782","部门":"办公室","职务":"所长","状态":"正常"},{"序号":"2","用户帐号":"lxw","用户姓名":"刘兴文","手机号码":"1393123782","部门":"办公室","职务":"副所长","状态":"正常"},{"序号":"3","用户帐号":"zhouj","用户姓名":"周静","手机号码":"1393123782","部门":"办公室","职务":"副所长","状态":"正常"},{"序号":"4","用户帐号":"jph","用户姓名":"蒋鹏华","手机号码":"1393123782","部门":"工程科","职务":"科长","状态":"正常"},{"序号":"5","用户帐号":"zhf","用户姓名":"詹华锋","手机号码":"1393123782","部门":"工程科","职务":"工作人员","状态":"正常"},{"序号":"6","用户帐号":"lj","用户姓名":"廖军","手机号码":"1393123782","部门":"工程科","职务":"工作人员","状态":"正常"},{"序号":"7","用户帐号":"fxm","用户姓名":"冯小明","手机号码":"1393123782","部门":"养护科","职务":"科长","状态":"正常"},{"序号":"8","用户帐号":"wqX","用户姓名":"王乾霄","手机号码":"1393123782","部门":"养护科","职务":"工作人员","状态":"正常"},{"序号":"9","用户帐号":"jwd","用户姓名":"焦卫东","手机号码":"1393123782","部门":"养护科","职务":"工作人员","状态":"正常"},{"序号":"10","用户帐号":"Ip","用户姓名":"李萍","手机号码":"1393123782","部门":"养护科","职务":"工作人员","状态":"冻结"}],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"records": [{"视频ID":"242","视频点名称":"大坝0+800坝外","所在区域":"库区","监控点类型":"枪机","ip端二]号":"172.10.118.9:10080","通道号":"12","经度":"112.107041","纬度":"30961038","视频序列号":"45d7f2047e46427399f90fa48983aa9e","创建日期":"2023-05-15"},{"视频ID":"243","视频点名称":"大坝1+500坝外","所在区域":"库区","监控点类型":"枪机","ip端二]号":"172.10.118.9:10080","通道号":"13","经度":"112.107041","纬度":"30 961038","视频序列号":"61e67523505941ef9af99aaec3bb0d1f","创建日期":"2023-05-05"},{"视频ID":"244","视频点名称":"管理所大门]","所在区域":"库区","监控点类型":"球机","ip端二]号":"172.10.118.9;10080","通道号":"14","经度":"112.107041","纬度":"30.961038","视频序列号":"351 23f16d6a04da3ae1330ffa82df764","创建日期":"2023-05-04"},{"视频ID":"245","视频点名称":"大坝1+300坝外","所在区域":"库区","监控点类型":"球机","ip端二]号":"172.10.118.9:10080","通道号":"15","经度":"112.107041","纬度":"30 961038","视频序列号":"35584a43ea2e43f99f4829430951172d","创建日期":"2023-04-28"},{"视频ID":"246","视频点名称":"大坝0+600","所在区域":"库区","监控点类型":"球机","ip端二]号":"172.10.118.9:10080","通道号":"16","经度":"112.107041","纬度":"30961038","视频序列号":"d371315a47f64ee498a32def37 7ccedb","创建日期":"2023-04-26"},{"视频ID":"247","视频点名称":"量水堰0+180","所在区域":"库区","监控点类型":"球机","ip端二]号":"172.10.118.9:10080","通道号":"17","经度":"112.107041","纬度":"30 961038","视频序列号":"0dda4a9c42984f99ae8e806e2e47ae4a","创建日期":"2023-04-17"},{"视频ID":"248","视频点名称":"量水堰0+400","所在区域":"库区","监控点类型":"枪机","ip端二]号":"172.10.118.9;10080","通道号":"18","经度":"112.107041","纬度":"30.961038","视频序列号":"db08a0857748494682fbe6648bb3e988","创建日期":"2023-04-14"},{"视频ID":"249","视频点名称":"量水堰1+200","所在区域":"库区","监控点类型":"枪机","ip端二]号":"172.10.118.9:10080","通道号":"19","经度":"112.107041","纬度":"30 961038","视频序列号":"c91d089bde6f411ebbf9823307628b96","创建日期":"2023-04-04"},{"视频ID":"250","视频点名称":"量水堰0+100","所在区域":"库区","监控点类型":"枪机","ip端二]号":"172.10.118.9:10080","通道号":"20","经度":"112.107041","纬度":"30961038","视频序列号":"2f70602983554c6b8073fecf1 ef8f18f","创建日期":"2023-03-25"},{"视频ID":"251","视频点名称":"溢洪道下游","所在区域":"库区","监控点类型":"枪机","ip端二]号":"172.10.118.9:10080","通道号":"21","经度":"112.107041","纬度":"30 961038","视频序列号":"e5d3d45b836e4b939ecf1 bc60f670ba4","创建日期":"2023-03-14"}],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"records": [{"序号":"1","行政区划编码":"420582102205000","行政区划名称":"乘马岗镇","创建时间":"2023-08-30"},{"序号":"2","行政区划编码":"420582102205102","行政区划名称":"乘马岗村","创建时间":"2023-08-30"},{"序号":"3","行政区划编码":"420582102205104","行政区划名称":"乘马岗村一组","创建时间":"2023-08-29"},{"序号":"4","行政区划编码":"420582102205105","行政区划名称":"乘马岗村二组","创建时间":"2023-08-29"},{"序号":"5","行政区划编码":"420582102206000","行政区划名称":"乘马岗村三组","创建时间":"2023-08-29"},{"序号":"6","行政区划编码":"420582102206101","行政区划名称":"乘马岗村四组","创建时间":"2023-08-26"},{"序号":"7","行政区划编码":"420582102206102","行政区划名称":"石槽冲村","创建时间":"2023-08-24"},{"序号":"8","行政区划编码":"420582102206103","行政区划名称":"石槽冲村一组","创建时间":"2023-08-18"},{"序号":"9","行政区划编码":"420582102206104","行政区划名称":"石槽冲村二组","创建时间":"2023-08-15"},{"序号":"10","行政区划编码":"420582102206105","行政区划名称":"石槽冲村三组","创建时间":"2023-08-12"}],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"records": [
|
||||||
|
{"闸阀代码":"HP0024208020000063","闸阀名称":"电动蝶阀1#","产品名称":"暗杆闸阀","经度":"114.989405","纬度":"31.36019","所在位置":"取水塔1楼","型号":"Z45H-PN10","规格":"DN40","结构长度":"GB/T12221-2005","创建日期":"2023-08-30"},
|
||||||
|
{"闸阀代码":"HP0024208020000078","闸阀名称":"电动蝶阀2#","产品名称":"暗杆闸阀","经度":"114.078096","纬度":"30.953825","所在位置":"取水塔2楼","型号":"Z45H-PN10","规格":"DN60","结构长度":"GB/T12221-2005","创建日期":"2023-08-30"},
|
||||||
|
{"闸阀代码":"HP0024208020000082","闸阀名称":"电动蝶阀3#","产品名称":"暗杆闸阀","经度":"114.118889","纬度":"30.974167","所在位置":"取水塔3楼","型号":"Z45H-PN10","规格":"DN80","结构长度":"GB/T12221-2005","创建日期":"2023-08-29"},
|
||||||
|
{"闸阀代码":"HP0024208020000085","闸阀名称":"电动活塞阀1#","产品名称":"电磁阀","经度":"114.989405","纬度":"31.36019","所在位置":"取水塔1楼","型号":"Z45H-PN10","规格":"DN40","结构长度":"GB/T12221-2005","创建日期":"2023-08-30"},
|
||||||
|
{"闸阀代码":"HP0024208020000087","闸阀名称":"电动活塞阀2#","产品名称":"电磁阀","经度":"114.078096","纬度":"30.953825","所在位置":"取水塔2楼","型号":"Z45H-PN10","规格":"DN60","结构长度":"GB/T12221-2005","创建日期":"2023-08-30"},
|
||||||
|
{"闸阀代码":"HP0024208020000089","闸阀名称":"电动活塞阀3#","产品名称":"电磁阀","经度":"114.118889","纬度":"30.974167","所在位置":"取水塔3楼","型号":"Z45H-PN10","规格":"DN80","结构长度":"GB/T12221-2005","创建日期":"2023-08-29"}
|
||||||
|
],
|
||||||
|
"total": 10,
|
||||||
|
"pages": 1
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,44 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="description" content="Web site created using create-react-app" />
|
||||||
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>小玉潭数据资源中心</title>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1660979818572" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1211" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
|
||||||
|
</style></defs><path d="M0 0m81.92 0l860.16 0q81.92 0 81.92 81.92l0 860.16q0 81.92-81.92 81.92l-860.16 0q-81.92 0-81.92-81.92l0-860.16q0-81.92 81.92-81.92Z" fill="#29BEFC" p-id="1212"></path><path d="M282.95168 623.616l10.48576 3.52256V324.89472h434.176v307.93728l11.30496-5.36576c14.90944-7.04512 27.40224-6.02112 43.8272-4.79232l8.51968 0.65536V261.16096h-98.54976V184.32h-364.544v76.84096H229.62176v364.87168l9.37984-1.76128c16.01536-2.94912 28.50816-5.77536 43.95008-0.65536z m109.1584-375.52128h236.83072v13.1072H392.11008z" fill="#FFFFFF" p-id="1213"></path><path d="M602.112 623.0016a93.96224 93.96224 0 0 1 52.18304 15.9744l12.288 8.192v-191.6928h-72.21248v167.5264zM363.84768 648.92928l9.13408-6.88128a93.88032 93.88032 0 0 1 46.4896-19.08736l7.00416-0.8192v-166.7072h-72.4992v189.27616zM477.02016 635.65824a92.44672 92.44672 0 0 1 9.87136 6.47168l29.4912 22.36416 29.12256-22.03648v-209.67424h-72.54016v200.704zM853.44256 768.73728l-26.05056-19.49696a84.86912 84.86912 0 0 0-101.62176 0l-25.96864 19.53792a16.384 16.384 0 0 1-19.74272 0l-26.0096-19.53792a84.95104 84.95104 0 0 0-101.70368 0l-25.88672 19.53792a16.67072 16.67072 0 0 1-19.82464 0l-25.96864-19.53792a84.95104 84.95104 0 0 0-101.66272 0l-25.96864 19.53792a16.5888 16.5888 0 0 1-19.78368 0L307.2 749.24032a84.91008 84.91008 0 0 0-101.66272 0l-33.9968 25.76384A33.95584 33.95584 0 0 0 212.992 828.86656l33.66912-25.43616a16.5888 16.5888 0 0 1 19.78368 0l25.92768 19.49696a84.74624 84.74624 0 0 0 101.62176 0l26.0096-19.49696a16.384 16.384 0 0 1 19.70176 0l25.35424 19.2512a86.50752 86.50752 0 0 0 76.47232 13.35296 83.92704 83.92704 0 0 0 25.88672-13.1072l26.0096-19.53792a16.384 16.384 0 0 1 19.74272 0l25.96864 19.49696a84.91008 84.91008 0 0 0 101.66272 0l25.96864-19.53792a16.384 16.384 0 0 1 19.74272 0l25.68192 19.29216q1.06496 0.90112 2.21184 1.67936a33.95584 33.95584 0 0 0 52.8384-33.792 33.54624 33.54624 0 0 0-13.80352-21.79072z" fill="#FFFFFF" p-id="1214"></path><path d="M863.88736 674.4064l-26.8288-20.15232a87.61344 87.61344 0 0 0-105.02144 0l-26.78784 20.15232a17.16224 17.16224 0 0 1-20.48 0l-26.78784-20.15232a87.6544 87.6544 0 0 0-105.02144 0l-26.8288 20.15232a17.08032 17.08032 0 0 1-20.48 0l-26.86976-20.15232a87.53152 87.53152 0 0 0-104.98048 0l-26.8288 20.15232a17.08032 17.08032 0 0 1-20.48 0l-26.78784-20.15232a87.57248 87.57248 0 0 0-104.98048 0l-34.77504 26.33728a35.06176 35.06176 0 0 0 42.3936 55.82848l34.65216-26.29632a17.08032 17.08032 0 0 1 20.48 0l26.86976 20.15232a87.69536 87.69536 0 0 0 104.98048 0l26.8288-20.15232a17.08032 17.08032 0 0 1 20.48 0l26.8288 20.11136a86.95808 86.95808 0 0 0 52.26496 17.57184h0.36864v-8.192 8.192a87.40864 87.40864 0 0 0 52.38784-17.57184l26.74688-20.15232a17.08032 17.08032 0 0 1 20.48 0l26.8288 20.15232a87.73632 87.73632 0 0 0 104.98048 0l26.8288-20.15232a17.08032 17.08032 0 0 1 20.48 0l26.8288 20.11136a35.06176 35.06176 0 1 0 42.35264-55.82848z" fill="#FFFFFF" p-id="1215"></path></svg>
|
||||||
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Group 21</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Ant-Design-Pro-3.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="账户密码登录-校验" transform="translate(-79.000000, -82.000000)">
|
||||||
|
<g id="Group-21" transform="translate(77.000000, 73.000000)">
|
||||||
|
<g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
|
||||||
|
<ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
|
||||||
|
<ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
|
||||||
|
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
|
||||||
|
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||||
|
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
||||||
|
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
||||||
|
<g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
|
||||||
|
<ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
|
||||||
|
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
|
||||||
|
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
|
||||||
|
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
|
||||||
|
<ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
|
||||||
|
<ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
|
||||||
|
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
|
||||||
|
<g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
|
||||||
|
<ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
|
||||||
|
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
|
||||||
|
</g>
|
||||||
|
<ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
|
||||||
|
<ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
|
||||||
|
<ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
|
||||||
|
<path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
|
||||||
|
</g>
|
||||||
|
<g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
|
||||||
|
<ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
|
||||||
|
<g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
|
||||||
|
<ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
|
||||||
|
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
|
||||||
|
</g>
|
||||||
|
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
|
||||||
|
<ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
|
||||||
|
<ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
|
||||||
|
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
|
||||||
|
</g>
|
||||||
|
<g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
|
||||||
|
<g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
|
||||||
|
<circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
|
||||||
|
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
|
||||||
|
</g>
|
||||||
|
<circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
|
||||||
|
<path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
|
||||||
|
<path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
|
||||||
|
<polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
|
||||||
|
<path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
|
||||||
|
<path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
|
||||||
|
<path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
|
||||||
|
<circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
|
||||||
|
<circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
|
||||||
|
<circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
|
||||||
|
<circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
|
||||||
|
<circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 466 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 325 B |
|
After Width: | Height: | Size: 319 B |
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { Breadcrumb } from 'antd';
|
||||||
|
import React, { useMemo } from 'react'
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { MenuItem } from '../../models/_/defs';
|
||||||
|
|
||||||
|
const AppBreadcrumb: React.FC<{
|
||||||
|
menu: MenuItem[];
|
||||||
|
menuIndexes: string[];
|
||||||
|
}> = ({ menu, menuIndexes }) => {
|
||||||
|
|
||||||
|
const menulist = useMemo(() => {
|
||||||
|
const ret: MenuItem[] = [];
|
||||||
|
let o: MenuItem[] = menu;
|
||||||
|
for (const id of menuIndexes) {
|
||||||
|
const hit = o.find((m: any) => m.id === id);
|
||||||
|
if (hit) {
|
||||||
|
ret.push(hit);
|
||||||
|
o = hit.children || [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}, [menu, menuIndexes])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="app-breadcrumb">
|
||||||
|
<Breadcrumb >
|
||||||
|
{
|
||||||
|
menulist.map((m: any) => (
|
||||||
|
<Breadcrumb.Item key={m.id}>
|
||||||
|
{
|
||||||
|
m.path ? <Link to={m.path}>{m.title}</Link> : m.title
|
||||||
|
}
|
||||||
|
</Breadcrumb.Item>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Breadcrumb>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppBreadcrumb
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { LogoutOutlined } from '@ant-design/icons';
|
||||||
|
import { Dropdown, Menu } from 'antd';
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
import User from '../../assets/icons/user.png';
|
||||||
|
import { LoginUser } from "../../models/auth/_";
|
||||||
|
import { Dispatch } from "../../models/store";
|
||||||
|
|
||||||
|
const HeaderUser: React.FC<{
|
||||||
|
user: LoginUser | null;
|
||||||
|
}> = ({ user }) => {
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch<Dispatch>();
|
||||||
|
|
||||||
|
const hasLogout = sessionStorage.getItem('__useToken') === '0';
|
||||||
|
|
||||||
|
const logout = () => {
|
||||||
|
dispatch.auth.logout();
|
||||||
|
navigate('/login');
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hasLogout) {
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
overlay={(
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item icon={<LogoutOutlined />} key="logout" onClick={logout}>退出登陆</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className='user-menu'>
|
||||||
|
<img className='user-menu-icon' src={User} />
|
||||||
|
{`欢迎您,${user?.name || ''}`}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</Dropdown>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className='user-menu'>
|
||||||
|
<img className='user-menu-icon' src={User} />
|
||||||
|
{`欢迎您,${user?.name || ''}`}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HeaderUser
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { Menu } from 'antd';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { useLocation, useNavigate } from 'react-router';
|
||||||
|
import { MenuItem } from '../../models/_/defs';
|
||||||
|
|
||||||
|
const { SubMenu } = Menu;
|
||||||
|
|
||||||
|
export function selectedMenu(menu: MenuItem[], menuIndexes: string[]) {
|
||||||
|
if (!menuIndexes || !menuIndexes[0]) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuItem = menu.find((o) => o.id === menuIndexes[0]);
|
||||||
|
return (menuItem && menuItem.children) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const SiderMenu: React.FC<{
|
||||||
|
menu: MenuItem[],
|
||||||
|
menuIndexes: string[],
|
||||||
|
}> = ({ menu, menuIndexes }) => {
|
||||||
|
const subMenu = useMemo(() => selectedMenu(menu, menuIndexes), [menu, menuIndexes]);
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
const pathname = location.pathname;
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
function goto(url: string) {
|
||||||
|
if (pathname !== url) {
|
||||||
|
navigate(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
key={menuIndexes[0] || '0'}
|
||||||
|
mode="inline"
|
||||||
|
selectedKeys={[`${menuIndexes[2] || menuIndexes[1]}`]}
|
||||||
|
defaultOpenKeys={[`${menuIndexes[1]}`]}
|
||||||
|
|
||||||
|
>
|
||||||
|
{
|
||||||
|
subMenu.map((o: any) => (
|
||||||
|
o.children && o.children.length > 0 ? (
|
||||||
|
<SubMenu key={o.id} icon={o.icon ? <img style={{ width: 16, height: 16 }} src={`/assets/icons/${o.icon}.png`} /> : undefined} title={o.title}>
|
||||||
|
{
|
||||||
|
o.children.map((oo: any) => (
|
||||||
|
<Menu.Item
|
||||||
|
onClick={() => goto(oo.path)} key={oo.id}>{oo.title}</Menu.Item>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</SubMenu>
|
||||||
|
) : (
|
||||||
|
<Menu.Item
|
||||||
|
icon={o.icon ? <img style={{ width: 16, height: 16 }} src={`/assets/icons/${o.icon}.png`} /> : undefined}
|
||||||
|
onClick={() => goto(o.path)} key={o.id}>{o.title}</Menu.Item>
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(SiderMenu);
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router';
|
||||||
|
import { MenuItem } from '../../models/_/defs';
|
||||||
|
|
||||||
|
|
||||||
|
function getMenuUrl(menuItem: MenuItem | undefined): string | null {
|
||||||
|
if (!menuItem) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = menuItem.path || menuItem.redirect;
|
||||||
|
if (url) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menuItem.children && menuItem.children.length) {
|
||||||
|
for (const m of menuItem.children) {
|
||||||
|
const url = getMenuUrl(m);
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const TopMenu: React.FC<{
|
||||||
|
menu: MenuItem[];
|
||||||
|
menuIndexes: string[];
|
||||||
|
}> = ({ menu, menuIndexes }) => {
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const menuClicked = (id: string) => {
|
||||||
|
const menuItem = menu.find(m => m.id == id);
|
||||||
|
const url = getMenuUrl(menuItem);
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
navigate(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='app-top-menu'>
|
||||||
|
{
|
||||||
|
menu?.map(o => (
|
||||||
|
<div
|
||||||
|
key={o.id}
|
||||||
|
className={`app-top-menu-item${menuIndexes[0] == o.id ? ' active' : ''}`}
|
||||||
|
onClick={() => menuClicked(o.id)}
|
||||||
|
>
|
||||||
|
<img src={`${process.env.PUBLIC_URL}/assets/icons2/${o.icon}`} />
|
||||||
|
{o.title}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(TopMenu);
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
import { BellOutlined } from '@ant-design/icons';
|
||||||
|
import { Layout } from 'antd';
|
||||||
|
import React, { Suspense, useEffect, useMemo } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { Outlet, useNavigate } from 'react-router';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import Icon1 from '../../assets/icons/icon.png';
|
||||||
|
import { SUBTITLE, TITLE, config } from '../../config';
|
||||||
|
import { findLeafMenu, findMenu } from '../../models/auth/menu';
|
||||||
|
import { Dispatch, RootState } from '../../models/store';
|
||||||
|
import { getParameter } from '../../utils/utils';
|
||||||
|
import HeaderUser from './HeaderUser';
|
||||||
|
import SiderMenu from './SiderMenu';
|
||||||
|
import TopMenu from './TopMenu';
|
||||||
|
import './style.less';
|
||||||
|
|
||||||
|
|
||||||
|
const { Header, Content, Sider } = Layout;
|
||||||
|
|
||||||
|
|
||||||
|
const DashboardLayout: React.FC = () => {
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const pathname = location.pathname;
|
||||||
|
|
||||||
|
const dispatch = useDispatch<Dispatch>();
|
||||||
|
|
||||||
|
const user = useSelector((state: RootState) => state.auth.user);
|
||||||
|
const menu = useSelector((state: RootState) => state.auth.menu);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const token = getParameter('token') || 'demo';
|
||||||
|
if (token) {
|
||||||
|
dispatch.auth.regByToken(token).then(() => {
|
||||||
|
dispatch.auth.loadMenu(undefined);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (user == null) {
|
||||||
|
navigate('/login')
|
||||||
|
} else {
|
||||||
|
dispatch.auth.loadMenu(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user === null) {
|
||||||
|
|
||||||
|
}
|
||||||
|
if (user === 'failed') {
|
||||||
|
// 跳到主页面
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
const [menuIndexes, leafMenu] = useMemo(() => [
|
||||||
|
findMenu(menu, pathname),
|
||||||
|
findLeafMenu(menu, pathname),
|
||||||
|
], [menu, pathname]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (leafMenu) {
|
||||||
|
// document.title = '小玉潭水库 · ' + leafMenu.title
|
||||||
|
document.title = leafMenu.title
|
||||||
|
}
|
||||||
|
}, [leafMenu]);
|
||||||
|
|
||||||
|
|
||||||
|
if (user === 'failed') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout className="app-root">
|
||||||
|
<Header className="app-header">
|
||||||
|
<a className='app-icon-a' >
|
||||||
|
<img alt="..." className="app-icon" src={Icon1} />
|
||||||
|
<span className="app-title">{SUBTITLE}</span>
|
||||||
|
</a>
|
||||||
|
<TopMenu menu={menu} menuIndexes={menuIndexes} />
|
||||||
|
<div className='flex-grow-1'></div>
|
||||||
|
|
||||||
|
<span className='user-menu' style={{ padding: '0 19px' }}>
|
||||||
|
<BellOutlined />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<HeaderUser user={user} />
|
||||||
|
</Header>
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
{
|
||||||
|
menuIndexes.length > 1 ? (
|
||||||
|
<Sider className="app-sider" width={208}>
|
||||||
|
<SiderMenu menu={menu} menuIndexes={menuIndexes} />
|
||||||
|
</Sider>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
<Content className="app-content">
|
||||||
|
<div className={menuIndexes.length > 1 ? 'content-root' : 'content-root-fs'}>
|
||||||
|
{
|
||||||
|
user ? (
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<Outlet />
|
||||||
|
</Suspense>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Content>
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DashboardLayout
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
.app-root {
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
|
||||||
|
.app-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 12px 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 20;
|
||||||
|
color: #fff !important;
|
||||||
|
background-image: url(../../assets/images/menu.png);
|
||||||
|
background-size: 100% 100%;
|
||||||
|
|
||||||
|
.app-icon-a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 10px;
|
||||||
|
|
||||||
|
.app-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #fff;
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
margin-right: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
.ant-menu {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-item-active {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-item-selected {
|
||||||
|
//background-color: rgba(70, 128, 221, .75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-item-selected::after {
|
||||||
|
border-bottom: 3px solid #86b9ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-horizonal>.ant-menu-item::after {
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
.app-top-menu {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.app-top-menu-item {
|
||||||
|
height: 64px;
|
||||||
|
padding: 0 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 18px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: rgba(70, 128, 221, .75);
|
||||||
|
border-bottom: 3px solid #86b9ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-menu {
|
||||||
|
display: flex;
|
||||||
|
height: 59px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 14px;
|
||||||
|
|
||||||
|
.user-menu-icon {
|
||||||
|
width: 28px;
|
||||||
|
margin: 20px 10px 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: hsla(0, 0%, 100%, .3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.app-sider {
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background-color: #fff !important;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-content {
|
||||||
|
z-index: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-root {
|
||||||
|
padding: 10px;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-root-fs {
|
||||||
|
padding: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-body {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,327 @@
|
||||||
|
.ant-btn-primary,
|
||||||
|
.ant-btn-default {
|
||||||
|
padding: 0 15px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-form-item-label>label {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-input,
|
||||||
|
.ant-select-selector {
|
||||||
|
border-radius: 4px !important;
|
||||||
|
// box-shadow: 0 0 2px 2px #f5f5f5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-input-search {
|
||||||
|
.ant-input {
|
||||||
|
border-radius: 4px 0 0 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn {
|
||||||
|
border-radius: 0 4px 4px 0 !important;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-radio-group {
|
||||||
|
.ant-radio-button-wrapper:first-child {
|
||||||
|
border-radius: 8px 0 0 8px !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-radio-button-wrapper:last-child {
|
||||||
|
border-radius: 0 8px 8px 0 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-radio-button-wrapper {
|
||||||
|
border: 1px solid @primary-color !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #D9EBFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-radio-button-wrapper {
|
||||||
|
color: #3B7CFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.common-style {
|
||||||
|
.ant-table-container {
|
||||||
|
border: 1px solid #e8e8e8 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-thead>tr>th,
|
||||||
|
.ant-table-tbody>tr>td,
|
||||||
|
.ant-table tfoot>tr>th,
|
||||||
|
.ant-table tfoot>tr>td {
|
||||||
|
padding: 12px 8px;
|
||||||
|
border-right: 1px solid transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-thead>tr>th {
|
||||||
|
background-color: #e0edff !important;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-tbody>tr:not(.ant-table-placeholder)>td {
|
||||||
|
border-top: 1px solid #e0edff;
|
||||||
|
border-bottom: 1px solid #e0edff;
|
||||||
|
|
||||||
|
&.ant-table-cell-row-hover {
|
||||||
|
background: #f4f8ff;
|
||||||
|
color: #3773c5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-tbody tr:not(.ant-table-placeholder):nth-child(2n) {
|
||||||
|
background: #f4f8ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-pagination {
|
||||||
|
|
||||||
|
|
||||||
|
.ant-pagination-item:not(.ant-pagination-item-active) {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-pagination-item-link {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-pagination-item-active {
|
||||||
|
background-color: #3773c5 !important;
|
||||||
|
border-radius: 99px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ant-modal-content {
|
||||||
|
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.ant-modal-header {
|
||||||
|
background: #2a66c2;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
|
||||||
|
.ant-modal-title {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-modal-close {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn-link.ant-btn-sm {
|
||||||
|
color: #3773c5;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anticon-close.ant-modal-close-icon {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.ant-table-column-sort {
|
||||||
|
background-color: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ant-modal.fullscreen {
|
||||||
|
width: 100vw !important;
|
||||||
|
max-width: 100vw !important;
|
||||||
|
top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
|
||||||
|
.ant-modal-body {
|
||||||
|
height: calc(100vh - 55px);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
.ant-table-thead>tr>th {
|
||||||
|
border-bottom: none !important;
|
||||||
|
|
||||||
|
background: @primary-color !important;
|
||||||
|
color: #fff !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-top-left-radius: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-top-right-radius: 16px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-tbody>tr>td {
|
||||||
|
border-bottom: none !important;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 3px 2px 3px 2px #E8ECF1;
|
||||||
|
padding: 12px 16px !important;
|
||||||
|
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-top-left-radius: 8px;
|
||||||
|
border-bottom-left-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-top-right-radius: 8px;
|
||||||
|
border-bottom-right-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ant-table table {
|
||||||
|
border-spacing: 0 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-pagination {
|
||||||
|
.ant-select-selector {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ant-pagination-item:not(.ant-pagination-item-active) {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-pagination-item-link {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-pagination-item-active {
|
||||||
|
background-color: @primary-color;
|
||||||
|
border-radius: 99px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-pagination-options-quick-jumper input {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn-primary,
|
||||||
|
.ant-btn-default {
|
||||||
|
min-width: 110px;
|
||||||
|
border-radius: 99px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-input,
|
||||||
|
.ant-select-selector {
|
||||||
|
border-radius: 4px !important;
|
||||||
|
box-shadow: 0 0 2px 2px #f5f5f5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-input-search {
|
||||||
|
.ant-input-lg {
|
||||||
|
border-top-left-radius: 99px !important;
|
||||||
|
border-bottom-left-radius: 99px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn-lg {
|
||||||
|
border-top-right-radius: 99px !important;
|
||||||
|
border-bottom-right-radius: 99px !important;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.kai-comp .ant-picker-range {
|
||||||
|
border-top-right-radius: 4px !important;
|
||||||
|
border-bottom-right-radius: 4px !important;
|
||||||
|
border-top-left-radius: 0px !important;
|
||||||
|
border-bottom-left-radius: 0px !important;
|
||||||
|
box-shadow: 0 0 2px 2px #f5f5f5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kai-comp .ant-picker-status-error.ant-picker {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-select {
|
||||||
|
.ant-tree {
|
||||||
|
border: 1px solid #f3f5f8;
|
||||||
|
background: #f9fafc;
|
||||||
|
border-radius: 4px !important;
|
||||||
|
padding: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-tree-switcher {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-tree-switcher {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #C7CBD3;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ant-radio-group {
|
||||||
|
.ant-radio-button-wrapper:first-child {
|
||||||
|
border-radius: 8px 0 0 8px !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-radio-button-wrapper:last-child {
|
||||||
|
border-radius: 0 8px 8px 0 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-radio-button-wrapper {
|
||||||
|
border: 1px solid @primary-color !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #D9EBFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-radio-button-wrapper {
|
||||||
|
color: #3B7CFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-checkbox-checked {
|
||||||
|
.ant-checkbox-inner {
|
||||||
|
background-color: #3b7cff !important;
|
||||||
|
border-color: #3b7cff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-checkbox-inner {
|
||||||
|
border-radius: 4px !important;
|
||||||
|
border-width: 1.4px !important;
|
||||||
|
border-color: #C7CBD3 !important;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { UploadOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Modal, Upload, UploadFile, UploadProps } from 'antd';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
const ImportModal: React.FC<{
|
||||||
|
visible: boolean;
|
||||||
|
close: () => void;
|
||||||
|
doImpt: (file: any) => Promise<void>;
|
||||||
|
accept: string;
|
||||||
|
}> = ({ visible, close, doImpt, accept }) => {
|
||||||
|
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
||||||
|
const [uploading, setUploading] = useState(false);
|
||||||
|
|
||||||
|
const handleUpload = async () => {
|
||||||
|
const file = fileList[0];
|
||||||
|
if (!file || uploading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setUploading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await doImpt(file);
|
||||||
|
close();
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
} finally {
|
||||||
|
setUploading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="数据导入"
|
||||||
|
open
|
||||||
|
okButtonProps={{
|
||||||
|
disabled: fileList.length === 0,
|
||||||
|
loading: uploading
|
||||||
|
}}
|
||||||
|
onOk={handleUpload}
|
||||||
|
onCancel={() => close()}
|
||||||
|
>
|
||||||
|
<Upload
|
||||||
|
multiple={false}
|
||||||
|
beforeUpload={file => {
|
||||||
|
setFileList([file]);
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
onRemove={() => {
|
||||||
|
setFileList([]);
|
||||||
|
}}
|
||||||
|
accept={accept}
|
||||||
|
maxCount={1}
|
||||||
|
onChange={(info) => {
|
||||||
|
console.log(info);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button icon={<UploadOutlined />}>选择文件</Button>
|
||||||
|
</Upload>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImportModal
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { CloseCircleOutlined, CloseOutlined, FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Modal, Space, Tabs } from 'antd';
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
const PopupDialog: React.FC<{
|
||||||
|
onClose: () => void;
|
||||||
|
width: number;
|
||||||
|
title: React.ReactNode;
|
||||||
|
data: any;
|
||||||
|
render: (data: any, fullScreen: boolean) => React.ReactNode;
|
||||||
|
}> = ({ data, width, title, onClose, render }) => {
|
||||||
|
|
||||||
|
const [fullscreen, setFullscreen] = useState(false);
|
||||||
|
|
||||||
|
const FullBtn = fullscreen ? FullscreenExitOutlined : FullscreenOutlined;
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open
|
||||||
|
title={(
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
{title}
|
||||||
|
<div style={{ flexGrow: 1 }}></div>
|
||||||
|
<Space>
|
||||||
|
<FullBtn style={{ cursor: 'pointer' }} onClick={() => setFullscreen(!fullscreen)} />
|
||||||
|
<CloseOutlined style={{ cursor: 'pointer' }} onClick={() => { onClose(); setFullscreen(false); }} />
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
className={fullscreen ? 'fullscreen' : undefined}
|
||||||
|
width={fullscreen ? undefined : width}
|
||||||
|
onCancel={onClose}
|
||||||
|
footer={null}
|
||||||
|
closable={false}
|
||||||
|
bodyStyle={{ padding: 0 }}
|
||||||
|
destroyOnClose
|
||||||
|
maskClosable={false}
|
||||||
|
>
|
||||||
|
{data ? render(data, fullscreen) : null}
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PopupDialog
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
import React from 'react'
|
||||||
|
import ReactEcharts from 'echarts-for-react';
|
||||||
|
|
||||||
|
|
||||||
|
export const colorLight = ['#00B9EF', '#36CBA9', '#6EDC8E', '#FACC14', '#da13d4', '#F04864', '#8543E0'];
|
||||||
|
|
||||||
|
|
||||||
|
export type StatItem = {
|
||||||
|
name: string;
|
||||||
|
percent?: number;
|
||||||
|
value: number;
|
||||||
|
data?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagsPieChart: React.FC<{
|
||||||
|
data: StatItem[];
|
||||||
|
total: number;
|
||||||
|
title: string;
|
||||||
|
loading?: boolean
|
||||||
|
}> = ({ data, total, title, loading }) => {
|
||||||
|
|
||||||
|
const option = React.useMemo(() => {
|
||||||
|
return {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: function (item: any) {
|
||||||
|
const { data } = item;
|
||||||
|
return `${data.name}: ${data.value}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: colorLight,
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
type: 'pie',
|
||||||
|
center: ['50%', '50%'],
|
||||||
|
radius: ['55%', '80%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
minAngle: 3,
|
||||||
|
itemStyle: {
|
||||||
|
borderWidth: 2,
|
||||||
|
borderColor: '#1f5593'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
normal: {
|
||||||
|
show: false,
|
||||||
|
position: 'center'
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
show: false,
|
||||||
|
textStyle: {
|
||||||
|
fontSize: '18',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
normal: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: data.map(item => {
|
||||||
|
if (item.value == 0) {
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%', width: '100%', position: 'relative' }}>
|
||||||
|
<ReactEcharts
|
||||||
|
option={option}
|
||||||
|
style={{ height: '100%', width: '100%' }}
|
||||||
|
/>
|
||||||
|
<div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', textAlign: 'center' }}>
|
||||||
|
<div style={{ fontSize: 16, color: '#eee8' }}>{title}</div>
|
||||||
|
<div style={{ fontSize: 16, color: '#eee', fontWeight: 'bold' }}>{total}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TagsPieChart
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export const MAIN_TEXT_CLR = '#3B4859'
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { Button, Popconfirm } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
import { CrudContext } from './useCrud';
|
||||||
|
|
||||||
|
|
||||||
|
const CancelCrud: React.FC<{
|
||||||
|
crudCtx: CrudContext<any>,
|
||||||
|
confirm?: boolean,
|
||||||
|
text?: string
|
||||||
|
}> = ({ crudCtx, confirm, text }) => {
|
||||||
|
text = text || '返回';
|
||||||
|
if (confirm) {
|
||||||
|
return (
|
||||||
|
<Popconfirm title="是否要放弃表单返回列表"
|
||||||
|
onConfirm={() => {
|
||||||
|
crudCtx.goto(null, null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button size="small" type="link">{text}</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Button onClick={() => crudCtx.goto(null, null)} size="small" type="link">{text}</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CancelCrud
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
export const DEF_INPUT_LEN = 464;
|
||||||
|
export const DEF_INPUT_LEN_QUARTS = 92;
|
||||||
|
|
||||||
|
export const DEF_LAYOUT = {
|
||||||
|
labelCol: { span: 8 },
|
||||||
|
wrapperCol: { span: 16 },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DEF_TAIL_LAYOUT = {
|
||||||
|
wrapperCol: { offset: 8, span: 16 },
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { DeleteOutlined, EditOutlined, QuestionCircleOutlined, UndoOutlined, FileSearchOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Popconfirm, Tooltip } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
danger?: boolean;
|
||||||
|
text?: string;
|
||||||
|
tooltip?: string;
|
||||||
|
onClick?: () => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OpButton: React.FC<IProps> = ({ tooltip, icon, danger, text, onClick }) => {
|
||||||
|
if (tooltip) {
|
||||||
|
return (
|
||||||
|
<Tooltip title={tooltip}>
|
||||||
|
<Button icon={icon} danger={danger} onClick={onClick} type="link" size='small'>{text}</Button>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button icon={icon} danger={danger} onClick={onClick} type="link" size='small'>{text}</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OpButton;
|
||||||
|
|
||||||
|
type ITypedProps = {
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DelOpButton: React.FC<ITypedProps> = ({ onClick }) => (
|
||||||
|
<Popconfirm title="确定删除?" icon={<QuestionCircleOutlined style={{ color: 'red' }} />} onConfirm={onClick}>
|
||||||
|
<OpButton text='删除' icon={<DeleteOutlined />} danger />
|
||||||
|
</Popconfirm>
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
export const EditOpButton: React.FC<ITypedProps> = ({ onClick }) =>
|
||||||
|
<OpButton text='编辑' icon={<EditOutlined />} onClick={onClick} />
|
||||||
|
|
||||||
|
export const ViewOpButton: React.FC<ITypedProps> = ({ onClick }) =>
|
||||||
|
<OpButton text='查看' icon={<FileSearchOutlined />} onClick={onClick} />
|
||||||
|
|
||||||
|
export const RestoreOpButton: React.FC<ITypedProps> = ({ onClick }) =>
|
||||||
|
<OpButton text='恢复' icon={<UndoOutlined />} onClick={onClick} />
|
||||||
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
export type BasicCrudType = 'add' | 'edit' | 'view';
|
||||||
|
|
||||||
|
export const CRUD_NAMES = {
|
||||||
|
add: '新增',
|
||||||
|
edit: '编辑'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CrudContext<modes = any> = {
|
||||||
|
mode: modes | null;
|
||||||
|
record: any;
|
||||||
|
goto: (mode: modes | null, r: any) => void;
|
||||||
|
loading: boolean;
|
||||||
|
setLoading: (val: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default function useCrud<modes = any>(params?: {
|
||||||
|
mode?: modes;
|
||||||
|
record?: any;
|
||||||
|
}): CrudContext<modes> {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const [state, setState] = useState<{
|
||||||
|
mode: modes | null;
|
||||||
|
record: any;
|
||||||
|
}>({
|
||||||
|
mode: params?.mode || null,
|
||||||
|
record: params?.record,
|
||||||
|
})
|
||||||
|
|
||||||
|
const goto = (mode: modes | null, record: any) => {
|
||||||
|
setState({ mode, record })
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
goto,
|
||||||
|
loading,
|
||||||
|
setLoading,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
import { TablePaginationConfig, TableProps } from 'antd';
|
||||||
|
import { useEffect, useReducer, useRef } from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
export type PageResult<T> = {
|
||||||
|
list: T[];
|
||||||
|
totalRow: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type SearchOption = {
|
||||||
|
pageSize?: number;
|
||||||
|
pageNumber?: number;
|
||||||
|
search?: { [key: string]: any };
|
||||||
|
sortField?: string;
|
||||||
|
sortOrder?: 'asc' | 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableState<T> = {
|
||||||
|
abort: boolean;
|
||||||
|
|
||||||
|
data: T[];
|
||||||
|
total: number;
|
||||||
|
loading: boolean;
|
||||||
|
|
||||||
|
pageSize: number;
|
||||||
|
pageNumber: number;
|
||||||
|
search: { [key: string]: string };
|
||||||
|
sortField?: string;
|
||||||
|
sortOrder?: 'asc' | 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type PageTableContext<T> = {
|
||||||
|
tableProps: TableProps<T>,
|
||||||
|
search: (params?: SearchOption) => void,
|
||||||
|
refresh: () => void,
|
||||||
|
initialSearch?: { [key: string]: string },
|
||||||
|
noRender: (index: number) => number;
|
||||||
|
searchParams: { [key: string]: string; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function usePageTable<T>(
|
||||||
|
service: (params?: SearchOption) => Promise<PageResult<T>>,
|
||||||
|
options: SearchOption = {}
|
||||||
|
): PageTableContext<T> {
|
||||||
|
const [, forceRender] = useReducer(s => s + 1, 1);
|
||||||
|
|
||||||
|
const state = useRef<TableState<T>>({
|
||||||
|
abort: false,
|
||||||
|
data: [],
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
pageSize: options.pageSize ?? 10,
|
||||||
|
pageNumber: options.pageNumber ?? 1,
|
||||||
|
sortField: options.sortField,
|
||||||
|
sortOrder: options.sortOrder,
|
||||||
|
search: options.search || {},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const stateRef = state.current;
|
||||||
|
stateRef.abort = false;
|
||||||
|
search();
|
||||||
|
|
||||||
|
return () => { stateRef.abort = true; }
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const search = (opt: SearchOption = {}) => {
|
||||||
|
const s = state.current;
|
||||||
|
if (s.loading || s.abort) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s.loading = true;
|
||||||
|
forceRender();
|
||||||
|
|
||||||
|
const pageParams = {
|
||||||
|
pageNumber: opt?.pageNumber ?? s.pageNumber,
|
||||||
|
pageSize: opt?.pageSize ?? s.pageSize,
|
||||||
|
sortField: opt?.sortField ?? s.sortField,
|
||||||
|
sortOrder: opt?.sortOrder ?? s.sortOrder,
|
||||||
|
search: opt?.search ?? s.search,
|
||||||
|
};
|
||||||
|
|
||||||
|
service(pageParams).then((data) => {
|
||||||
|
if (!s.abort) {
|
||||||
|
s.search = pageParams.search;
|
||||||
|
s.sortField = pageParams.sortField;
|
||||||
|
s.sortOrder = pageParams.sortOrder;
|
||||||
|
s.pageNumber = pageParams.pageNumber;
|
||||||
|
s.pageSize = pageParams.pageSize;
|
||||||
|
s.data = data.list;
|
||||||
|
s.total = data.totalRow;
|
||||||
|
s.loading = false;
|
||||||
|
|
||||||
|
forceRender();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTableChange = (pagination: any, filters: any, sort: any) => {
|
||||||
|
const { field, order } = sort;
|
||||||
|
if (field && order) {
|
||||||
|
const sortOrder: any = order === 'ascend' ? 'asc' : 'desc';
|
||||||
|
search({ pageNumber: pagination.current, pageSize: pagination.pageSize, sortOrder, sortField: field });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
search({ pageNumber: pagination.current, pageSize: pagination.pageSize });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
tableProps: {
|
||||||
|
bordered: true,
|
||||||
|
dataSource: state.current.data,
|
||||||
|
pagination: {
|
||||||
|
pageSize: state.current.pageSize,
|
||||||
|
current: state.current.pageNumber,
|
||||||
|
total: state.current.total,
|
||||||
|
...DefaultPaginationParam,
|
||||||
|
},
|
||||||
|
loading: state.current.loading,
|
||||||
|
onChange: handleTableChange,
|
||||||
|
},
|
||||||
|
search: (params?: SearchOption) => search({ ...params, pageNumber: 1 }),
|
||||||
|
refresh: search,
|
||||||
|
initialSearch: options?.search,
|
||||||
|
noRender: (index) => (state.current.pageNumber - 1) * state.current.pageSize + (index + 1),
|
||||||
|
searchParams: state.current.search,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default usePageTable;
|
||||||
|
|
||||||
|
|
||||||
|
export const DefaultPaginationParam: TablePaginationConfig = {
|
||||||
|
showTotal: (total: number, range: [number, number]) => `${range[0]}-${range[1]} 共${total}条`,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
size: 'small'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const timeAxisX = {
|
||||||
|
type: 'time',
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#1187cd',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
interval: 0,
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#1187cd44',
|
||||||
|
type: [5, 5]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enumAxisX = (values: string[], formatter?: boolean) => {
|
||||||
|
return {
|
||||||
|
data: values,
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#1187cd',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#1187cd44',
|
||||||
|
type: [5, 5]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
fontSize: 18,
|
||||||
|
formatter: formatter === false ? undefined : charLongNameFormatter
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const unitAsNameY = (unit: string, right?: boolean) => {
|
||||||
|
return {
|
||||||
|
name: unit,
|
||||||
|
nameGap: -6,
|
||||||
|
nameTextStyle: {
|
||||||
|
padding: right ? [0, 24, 0, 0] : [0, 0, 0, 16],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const charLongNameFormatter = (value: string) => {
|
||||||
|
var strs = value.split('');
|
||||||
|
let ret = '';
|
||||||
|
for (let i = 0; strs[i]; i += 1) {
|
||||||
|
if (i > 0) {
|
||||||
|
ret += '\n';
|
||||||
|
}
|
||||||
|
ret += strs[i];
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const valueAxisY = {
|
||||||
|
type: 'value',
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#1187cd44',
|
||||||
|
type: [5, 5]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
margin: 20,
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
nameTextStyle: {
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const valueAxisYNoLabel = {
|
||||||
|
type: 'value',
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#1187cd44',
|
||||||
|
type: [5, 5]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const minmaxMarkPoint = {
|
||||||
|
label: {
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{ type: 'max', name: 'Max', label: { fontSize: 18, textBorderColor: '#000', textBorderWidth: 1 } },
|
||||||
|
{ type: 'min', name: 'Min', label: { fontSize: 18, textBorderColor: '#000', textBorderWidth: 1 } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const labelLegend = {
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
itemWidth: 24,
|
||||||
|
itemHeight: 14,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const containLabelGrid = (top?: number) => {
|
||||||
|
return {
|
||||||
|
left: 10,
|
||||||
|
right: 10,
|
||||||
|
bottom: 10,
|
||||||
|
top: top || 30,
|
||||||
|
containLabel: true,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const smoothAreaLine = (colorSharpFormat: string) => {
|
||||||
|
return {
|
||||||
|
type: 'line',
|
||||||
|
color: colorSharpFormat,
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const smoothAreaLineEx = (colorSharpFormat: string) => {
|
||||||
|
return {
|
||||||
|
type: 'line',
|
||||||
|
color: colorSharpFormat,
|
||||||
|
smooth: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const gradientBarWithLabel = {
|
||||||
|
type: 'bar',
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
textBorderWidth: 0,
|
||||||
|
fontSize: 12,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
export const StationConsts: any = {
|
||||||
|
ctrlType: [
|
||||||
|
{ label: 'PLC', value: 'PLC' },
|
||||||
|
{ label: 'RTU', value: 'RTU' }
|
||||||
|
],
|
||||||
|
ctrlProtocol: [
|
||||||
|
{ label: '楚禹', value: '楚禹' },
|
||||||
|
{ label: '徐州伟思', value: '徐州伟思' },
|
||||||
|
{ label: 'PLC', value: 'PLC' },
|
||||||
|
{ label: '722', value: '722' }
|
||||||
|
],
|
||||||
|
wagaType: [
|
||||||
|
{ label: '分水闸', value: '分水闸' },
|
||||||
|
{ label: '泄洪闸', value: '泄洪闸' },
|
||||||
|
{ label: '节制闸', value: '节制闸' },
|
||||||
|
{ label: '排水闸', value: '排水闸' },
|
||||||
|
{ label: '退水闸', value: '退水闸' },
|
||||||
|
{ label: '引(进)水闸', value: '引(进)水闸' },
|
||||||
|
{ label: '挡潮闸', value: '挡潮闸' },
|
||||||
|
{ label: '船闸', value: '船闸' },
|
||||||
|
{ label: '涵闸', value: '涵闸' },
|
||||||
|
{ label: '其它', value: '其它' },
|
||||||
|
],
|
||||||
|
gaType: {
|
||||||
|
"waga": "水闸",
|
||||||
|
"slcga": "涵闸"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
export const TITLE = '小玉潭水库综合管理'
|
||||||
|
export const SUBTITLE = '数据资源中心';
|
||||||
|
|
||||||
|
const address = 'zhsz';
|
||||||
|
|
||||||
|
const extent: [number, number, number, number] = [
|
||||||
|
111.4151642480001101, 30.2719341291568753, 112.5877217125527068, 31.7067788077957857
|
||||||
|
];
|
||||||
|
|
||||||
|
const ADCD6 = '420802';
|
||||||
|
|
||||||
|
const NOAUTH_REDIRECT = 'http://219.138.108.99:30046/user/login?redirect=';
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
address,
|
||||||
|
title: '数据资源中心',
|
||||||
|
extent,
|
||||||
|
ADCD6,
|
||||||
|
|
||||||
|
transitionDuaration: 500,
|
||||||
|
|
||||||
|
NOAUTH_REDIRECT
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CTRL_DLG_WIDTH = 1800;
|
||||||
|
export const CHAN_DLG_WIDTH = 1200;
|
||||||
|
|
||||||
|
export const SM4_KEY = 'zo7v0xb9erg2pwqc';
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
@import '~antd/dist/antd.less';
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#root {
|
||||||
|
min-width: 1280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-grow-1 {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Password Dots Regular";
|
||||||
|
src: url('./assets/fonts/password.ttf');
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
:after,
|
||||||
|
:before {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
height: 8px;
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #d6dde9;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #c4cfe1;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
color: #fff;
|
||||||
|
background: #3b7cff;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/client';
|
||||||
|
import { ConfigProvider } from 'antd';
|
||||||
|
import zhCN from 'antd/lib/locale/zh_CN';
|
||||||
|
import 'moment/locale/zh-cn';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { HashRouter } from 'react-router-dom';
|
||||||
|
import { store } from './models/store';
|
||||||
|
import AppRouters from './views/AppRouters';
|
||||||
|
import './index.less';
|
||||||
|
import './components/ant_override.less'
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(
|
||||||
|
document.getElementById('root') as HTMLElement
|
||||||
|
);
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<HashRouter>
|
||||||
|
<ConfigProvider locale={zhCN}>
|
||||||
|
<AppRouters />
|
||||||
|
</ConfigProvider>
|
||||||
|
</HashRouter>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
export type MenuItem = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
path?: string;
|
||||||
|
redirect?: string;
|
||||||
|
icon?: string;
|
||||||
|
children?: MenuItem[];
|
||||||
|
}
|
||||||
|
export type SysUserItem = {
|
||||||
|
用户账号: string;
|
||||||
|
用户姓名: string;
|
||||||
|
手机号码: string;
|
||||||
|
部门: string;
|
||||||
|
职务: string;
|
||||||
|
状态: string;
|
||||||
|
}
|
||||||
|
export type DepartmentItem = {
|
||||||
|
部门编码: string;
|
||||||
|
部门名称: string;
|
||||||
|
上级部门: string;
|
||||||
|
排序号: string;
|
||||||
|
备注: string;
|
||||||
|
创建日期: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ResItem = {
|
||||||
|
水库代码: string;
|
||||||
|
水库名称: string;
|
||||||
|
测站编码: string;
|
||||||
|
行政区划: string;
|
||||||
|
经度: string;
|
||||||
|
纬度: string;
|
||||||
|
水库规模: string;
|
||||||
|
集雨面积: string;
|
||||||
|
总库容: string;
|
||||||
|
创建日期: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RivlItem = {
|
||||||
|
"河流代码": string,
|
||||||
|
"河流名称": string,
|
||||||
|
"河流长度": string,
|
||||||
|
"最大水深": string,
|
||||||
|
"经度": string,
|
||||||
|
"纬度": string,
|
||||||
|
"河源所在位置": string,
|
||||||
|
"河流流域面积": string,
|
||||||
|
"承雨面积": string,
|
||||||
|
"创建日期": string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DamItem = {
|
||||||
|
"大坝代码": string,
|
||||||
|
"大坝名称": string,
|
||||||
|
"是否主坝": string,
|
||||||
|
"经度": string,
|
||||||
|
"纬度": string,
|
||||||
|
"所在位置": string,
|
||||||
|
"最大坝高": string,
|
||||||
|
"坝顶长度": string,
|
||||||
|
"坝顶宽度": string,
|
||||||
|
"创建日期": string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ElStItem = {
|
||||||
|
"水电站代码": string,
|
||||||
|
"水电站名称": string,
|
||||||
|
"水电站类型": string,
|
||||||
|
"经度": string,
|
||||||
|
"纬度": string,
|
||||||
|
"装机容量": string,
|
||||||
|
"多年平均发电量": string,
|
||||||
|
"地点": string,
|
||||||
|
"创建日期": string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DirItem = {
|
||||||
|
"id": string,
|
||||||
|
"serviceTypeId": string,
|
||||||
|
"serviceName": string,
|
||||||
|
"describe": string,
|
||||||
|
"serviceAddr": string,
|
||||||
|
"port": string,
|
||||||
|
"route": string,
|
||||||
|
"registerTime": string,
|
||||||
|
"provider": string,
|
||||||
|
"stNum": string,
|
||||||
|
"contacts": string,
|
||||||
|
"telephone": string,
|
||||||
|
"target": string,
|
||||||
|
"enable": string,
|
||||||
|
"maxTime": string,
|
||||||
|
"countVo": {
|
||||||
|
"allCount": number,
|
||||||
|
"todayCount": number,
|
||||||
|
"weekCount": number,
|
||||||
|
"monthCount": number,
|
||||||
|
"yearCount": number,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ServType = {
|
||||||
|
"服务类型名称": string,
|
||||||
|
"服务类型描述": string,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type StItem = {
|
||||||
|
站名: string;
|
||||||
|
管理单位: string;
|
||||||
|
RTU编码: string;
|
||||||
|
经度: string;
|
||||||
|
纬度: string;
|
||||||
|
基础准高程: string;
|
||||||
|
站类: string;
|
||||||
|
归属协议: string;
|
||||||
|
创建日期: string;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
import { message } from "antd";
|
||||||
|
import { MenuItem } from "../_/defs";
|
||||||
|
|
||||||
|
export type LoginUser = {
|
||||||
|
id: number;
|
||||||
|
loginName: string;
|
||||||
|
name: string;
|
||||||
|
roleList: string[];
|
||||||
|
tokenInfo: {
|
||||||
|
tokenValue: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AuthState = {
|
||||||
|
user: LoginUser | null | 'failed';
|
||||||
|
menu: MenuItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const USER_SESSION_KEY = '__usereinfo__';
|
||||||
|
|
||||||
|
export function removeLoginInfo() {
|
||||||
|
sessionStorage.removeItem(USER_SESSION_KEY);
|
||||||
|
sessionStorage.removeItem('TOKEN');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLoginInfo(userInfo: string, token: string) {
|
||||||
|
sessionStorage.setItem(USER_SESSION_KEY, userInfo);
|
||||||
|
sessionStorage.setItem('TOKEN', token);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserFromSession(): LoginUser | null {
|
||||||
|
const strUser = sessionStorage.getItem(USER_SESSION_KEY);
|
||||||
|
if (!strUser) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const obj: LoginUser = JSON.parse(strUser);
|
||||||
|
if (obj.id && obj.tokenInfo.tokenValue) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function regByToken(token: string): Promise<LoginUser | undefined> {
|
||||||
|
const result: LoginUser | null = {
|
||||||
|
id: 1,
|
||||||
|
loginName: 'admin',
|
||||||
|
name: '管理员',
|
||||||
|
roleList: [],
|
||||||
|
tokenInfo: {
|
||||||
|
tokenValue: 'demo-token'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoginInfo(JSON.stringify(result), result.tokenInfo?.tokenValue);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function login(form: { username: string, password: string }): Promise<LoginUser | undefined> {
|
||||||
|
message.error('登陆失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function loadMenu(user: LoginUser): MenuItem[] {
|
||||||
|
|
||||||
|
const id = (() => {
|
||||||
|
let id = 1;
|
||||||
|
return () => `${id++}`
|
||||||
|
})();
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: id(), title: '资源目录', redirect: '/mgr/res', icon: 'xtgl.png', children: [
|
||||||
|
{
|
||||||
|
id: id(), title: '工程及水系', redirect: '/mgr/res', children: [
|
||||||
|
{ id: id(), title: '水库', path: '/mgr/res' },
|
||||||
|
{ id: id(), title: '河流', path: '/mgr/rivl' },
|
||||||
|
{ id: id(), title: '大坝', path: '/mgr/dam' },
|
||||||
|
// { id: id(), title: '水电站', path: '/mgr/elst' },
|
||||||
|
{ id: id(), title: '闸阀', path: '/mgr/gate' },
|
||||||
|
{ id: id(), title: '量水堰', path: '/mgr/wfm' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: id(), title: '基础数据管理', redirect: '/mgr/st', children: [
|
||||||
|
{ id: id(), title: '监测站点管理', path: '/mgr/st' },
|
||||||
|
{ id: id(), title: '视频点', path: '/mgr/video' },
|
||||||
|
//{ id: id(), title: '水位流量曲线', path: '/mgr/rzhis' },
|
||||||
|
{ id: id(), title: '管理单位', path: '/mgr/unit' },
|
||||||
|
{ id: id(), title: '行政区划', path: '/mgr/ad' },
|
||||||
|
{ id: id(), title: '人员管理', path: '/mgr/personel' },
|
||||||
|
{ id: id(), title: '部门管理', path: '/mgr/department' },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: id(), title: '数据交换共享', redirect: '/mgr/dir', icon: 'xtgl.png', children: [
|
||||||
|
{ id: id(), title: '目录管理', path: '/mgr/dir' },
|
||||||
|
{ id: id(), title: '发布及共享服务', path: '/mgr/servreg' },
|
||||||
|
{ id: id(), title: '元数据管理', path: '/mgr/servmgr' },
|
||||||
|
{ id: id(), title: '运维管理', path: '/mgr/servwatch' },
|
||||||
|
{ id: id(), title: '数据可视化', path: '/mgr/datahis' },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
].filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defaultHomePage() {
|
||||||
|
return '/mgr/sk';
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { createModel } from "@rematch/core";
|
||||||
|
import { RootModel } from "..";
|
||||||
|
import { MenuItem } from "../_/defs";
|
||||||
|
import { AuthState, getUserFromSession, loadMenu, login, LoginUser, regByToken, removeLoginInfo } from "./_";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const auth = createModel<RootModel>()({
|
||||||
|
state: {
|
||||||
|
user: getUserFromSession(),
|
||||||
|
menu: [],
|
||||||
|
} as AuthState,
|
||||||
|
|
||||||
|
reducers: {
|
||||||
|
setUser(state, user: LoginUser | null | 'failed'): AuthState {
|
||||||
|
return { ...state, user };
|
||||||
|
},
|
||||||
|
|
||||||
|
setMenu(state, menu: MenuItem[]): AuthState {
|
||||||
|
return { ...state, menu };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
effects: {
|
||||||
|
async regByToken(token: string): Promise<boolean> {
|
||||||
|
const result = await regByToken(token);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
this.setUser(result);
|
||||||
|
sessionStorage.setItem('__useToken', '1');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setUser('failed');
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
async login(form: { username: string, password: string }): Promise<boolean> {
|
||||||
|
const result = await login(form);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
this.setUser(result);
|
||||||
|
sessionStorage.setItem('__useToken', '0');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
removeLoginInfo();
|
||||||
|
this.setUser(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadMenu(payload, s) {
|
||||||
|
if (s.auth.user && s.auth.user !== 'failed') {
|
||||||
|
this.setMenu(loadMenu(s.auth.user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { MenuItem } from "../_/defs";
|
||||||
|
|
||||||
|
export function findMenu(menus: MenuItem[], pathname: string) {
|
||||||
|
if (!menus) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const m1 of menus) {
|
||||||
|
if (m1.path === pathname) {
|
||||||
|
return [m1.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m1.children && m1.children.length) {
|
||||||
|
for (const m2 of m1.children) {
|
||||||
|
if (m2.path === pathname) {
|
||||||
|
return [m1.id, m2.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m2.children && m2.children.length) {
|
||||||
|
for (const m3 of m2.children) {
|
||||||
|
if (m3.path === pathname) {
|
||||||
|
return [m1.id, m2.id, m3.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findLeafMenu(menus: MenuItem[], pathname: string) {
|
||||||
|
if (!menus) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const m1 of menus) {
|
||||||
|
if (m1.path === pathname) {
|
||||||
|
return m1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m1.children && m1.children.length) {
|
||||||
|
for (const m2 of m1.children) {
|
||||||
|
if (m2.path === pathname) {
|
||||||
|
return m2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m2.children && m2.children.length) {
|
||||||
|
for (const m3 of m2.children) {
|
||||||
|
if (m3.path === pathname) {
|
||||||
|
return m3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Models } from "@rematch/core"
|
||||||
|
import { auth } from "./auth";
|
||||||
|
|
||||||
|
export interface RootModel extends Models<RootModel> {
|
||||||
|
auth: typeof auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const models: RootModel = {
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { init, RematchDispatch, RematchRootState } from '@rematch/core'
|
||||||
|
import { models, RootModel } from './index'
|
||||||
|
|
||||||
|
export const store = init({
|
||||||
|
models,
|
||||||
|
})
|
||||||
|
|
||||||
|
export type Store = typeof store
|
||||||
|
export type Dispatch = RematchDispatch<RootModel>
|
||||||
|
export type RootState = RematchRootState<RootModel>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/// <reference types="react-scripts" />
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
export default class CachePromise<DataType = any> {
|
||||||
|
expireTime: number;
|
||||||
|
curTm: number = 0;
|
||||||
|
promise: Promise<DataType> | undefined;
|
||||||
|
pf: () => Promise<DataType>;
|
||||||
|
|
||||||
|
constructor(pf: () => Promise<DataType>, expireTime?: number) {
|
||||||
|
this.pf = pf;
|
||||||
|
this.expireTime = expireTime || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
setNull = () => {
|
||||||
|
this.promise = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
get = async () => {
|
||||||
|
const now = Date.now();
|
||||||
|
if (this.expireTime && now - this.curTm > this.expireTime) {
|
||||||
|
this.promise = this.pf();
|
||||||
|
this.curTm = now;
|
||||||
|
}
|
||||||
|
if (!this.promise) {
|
||||||
|
this.promise = this.pf();
|
||||||
|
this.curTm = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await this.promise;
|
||||||
|
if (!data) {
|
||||||
|
this.setNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
export function wait(t?: number, data?: any): Promise<any> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(data);
|
||||||
|
}, t || 1000);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function getBasePath(): string {
|
||||||
|
if (process.env.PUBLIC_URL.startsWith('http')) {
|
||||||
|
return process.env.PUBLIC_URL;
|
||||||
|
} else {
|
||||||
|
return window.location.origin + process.env.PUBLIC_URL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function parseGeoJSONFeature(data: any): any {
|
||||||
|
if (typeof data.lgtd === 'number' && typeof data.lttd === 'number') {
|
||||||
|
return {
|
||||||
|
type: "Feature",
|
||||||
|
properties: data,
|
||||||
|
geometry: {
|
||||||
|
type: "Point",
|
||||||
|
coordinates: [data.lgtd, data.lttd]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (data.geometry) {
|
||||||
|
let geometryObj = data.geometry;
|
||||||
|
if (typeof geometryObj === 'string') {
|
||||||
|
try {
|
||||||
|
geometryObj = JSON.parse(geometryObj);
|
||||||
|
} catch (e) {
|
||||||
|
geometryObj = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (geometryObj?.type) {
|
||||||
|
return {
|
||||||
|
type: "Feature",
|
||||||
|
properties: data,
|
||||||
|
geometry: geometryObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
type: "Feature",
|
||||||
|
properties: data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseGeoJSON(data: any): any {
|
||||||
|
const ret: any = {
|
||||||
|
type: "FeatureCollection",
|
||||||
|
crs: {
|
||||||
|
type: "name",
|
||||||
|
properties: {
|
||||||
|
name: "urn:ogc:def:crs:OGC:1.3:CRS84"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
features: []
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
ret.features = data.map(parseGeoJSONFeature);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
import { message } from "antd";
|
||||||
|
import axios, { AxiosRequestConfig } from "axios";
|
||||||
|
import { SearchOption } from "../components/crud/usePageTable";
|
||||||
|
|
||||||
|
// 此文件根据后端接口的实际规则进行调整
|
||||||
|
|
||||||
|
|
||||||
|
export const XINDA_TOKEN = 'XINDA_TOKEN';
|
||||||
|
|
||||||
|
let lastLoginErrorMsg = 0;
|
||||||
|
function loginError(data: any) {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
if (now - lastLoginErrorMsg < 3000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastLoginErrorMsg = now;
|
||||||
|
|
||||||
|
if (
|
||||||
|
data.msg.startsWith('Token已被顶下线') ||
|
||||||
|
data.msg.startsWith('Token已被踢下线')
|
||||||
|
) {
|
||||||
|
message.error('该账号已在别处登录');
|
||||||
|
} else if (
|
||||||
|
data.msg.startsWith('Token已过期')
|
||||||
|
) {
|
||||||
|
message.error('登陆已过期,请重新登陆');
|
||||||
|
} else {
|
||||||
|
message.error('登陆信息异常,请重新登陆');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function paginate(url: string, params: SearchOption = {}) {
|
||||||
|
const { search, ...pagerParams } = params;
|
||||||
|
|
||||||
|
const reqParams: any = {
|
||||||
|
...search,
|
||||||
|
...pagerParams
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await httpPost(url, reqParams);
|
||||||
|
if (!data) {
|
||||||
|
message.error('请求失败')
|
||||||
|
return { list: [], totalRow: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { list: data.records || [], totalRow: data.total ?? 0 }
|
||||||
|
} catch (e) {
|
||||||
|
return { list: [], totalRow: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function paginateDemo(fileName: string, params: SearchOption = {}) {
|
||||||
|
const data = await fetch(`${process.env.PUBLIC_URL}/demodata/${fileName}`).then(resp => resp.json());
|
||||||
|
if (!data) {
|
||||||
|
message.error('请求失败')
|
||||||
|
return { list: [], totalRow: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { list: data.records || [], totalRow: data.total ?? 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchListDemo(fileName: string) {
|
||||||
|
const data = await fetch(`${process.env.PUBLIC_URL}/demodata/${fileName}`).then(resp => resp.json());
|
||||||
|
if (!data) {
|
||||||
|
message.error('请求失败')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function httpPost(url: string, params: Object | string, config?: AxiosRequestConfig<Object>) {
|
||||||
|
try {
|
||||||
|
// const token = sessionStorage.getItem('TOKEN');
|
||||||
|
const resp = await axios.post(url, params, config);
|
||||||
|
const data = resp.data;
|
||||||
|
if (data.code === 200) {
|
||||||
|
return data.data || true;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (data.code === 401) {
|
||||||
|
const dispatch: Dispatch | undefined = (window as any).__dispatch__;
|
||||||
|
if (dispatch) {
|
||||||
|
dispatch.auth.logout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (data.code === 401 && data.msg) {
|
||||||
|
loginError(data);
|
||||||
|
} else if (typeof data.msg === 'string') {
|
||||||
|
message.error(data.msg);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function httpGet(url: string, params?: any) {
|
||||||
|
try {
|
||||||
|
// const token = sessionStorage.getItem('TOKEN');
|
||||||
|
const resp = await axios.get(url, {
|
||||||
|
params,
|
||||||
|
// headers: { Authorization: `Bearer ${token}` }
|
||||||
|
});
|
||||||
|
const data = resp.data;
|
||||||
|
if (data.code === 200) {
|
||||||
|
return data.data;
|
||||||
|
}
|
||||||
|
if (data.code === 401 && data.msg) {
|
||||||
|
loginError(data);
|
||||||
|
} else if (typeof data.msg === 'string') {
|
||||||
|
message.error(data.msg);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (data.code === 401) {
|
||||||
|
const dispatch: Dispatch | undefined = (window as any).__dispatch__;
|
||||||
|
if (dispatch) {
|
||||||
|
dispatch.auth.logout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const download = async (url: string, params: any) => {
|
||||||
|
|
||||||
|
let parts = [];
|
||||||
|
if (typeof params !== 'string') {
|
||||||
|
for (const key in params) {
|
||||||
|
const value = params[key];
|
||||||
|
|
||||||
|
parts.push(`${key}=${encodeURIComponent(`${value}`)}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parts.push(params);
|
||||||
|
}
|
||||||
|
console.log(`${url}?${parts.join('&')}`);
|
||||||
|
window.open(`${url}?${parts.join('&')}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
const useRefresh = (interval: number, params?: any) => {
|
||||||
|
const [t, setT] = useState(Date.now());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let h: any = null;
|
||||||
|
if (interval) {
|
||||||
|
h = setInterval(() => {
|
||||||
|
setT(Date.now());
|
||||||
|
}, interval);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (interval) {
|
||||||
|
clearInterval(h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [interval, params]);
|
||||||
|
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useRefresh;
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { useState, useEffect, useRef, useReducer } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
export type RequestContext<DataType> = {
|
||||||
|
data?: DataType;
|
||||||
|
error?: any;
|
||||||
|
loading: boolean;
|
||||||
|
refresh: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param p
|
||||||
|
* @returns
|
||||||
|
* @todo deal refresh when loading
|
||||||
|
*/
|
||||||
|
const useRequest = <DataType = any>(p: () => Promise<DataType>, dependences?: any[]): RequestContext<DataType> => {
|
||||||
|
const [data, setData] = useState<DataType | undefined>()
|
||||||
|
const [error, setError] = useState<any>();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const abort = useRef(false);
|
||||||
|
const [_, refresh] = useReducer(s => s + 1, 0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const doFetch = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
abort.current = false;
|
||||||
|
try {
|
||||||
|
const data = await p();
|
||||||
|
if (!abort.current) {
|
||||||
|
setData(data);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!abort.current) {
|
||||||
|
setError(e);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (!abort.current) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
doFetch();
|
||||||
|
return () => {
|
||||||
|
abort.current = true;
|
||||||
|
};
|
||||||
|
}, dependences ? [...dependences, _] : [_]);
|
||||||
|
|
||||||
|
return { data, error, loading, refresh };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useRequest;
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import useResizeObserver from '@react-hook/resize-observer';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function useSize(target: React.RefObject<HTMLElement>) {
|
||||||
|
const [size, setSize] = React.useState<DOMRect>();
|
||||||
|
|
||||||
|
React.useLayoutEffect(() => {
|
||||||
|
setSize(target.current?.getBoundingClientRect())
|
||||||
|
}, [target]);
|
||||||
|
|
||||||
|
useResizeObserver(target, (entry) => setSize(entry.contentRect))
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
import moment from "moment";
|
||||||
|
|
||||||
|
export function changeObjectStringToMoment(obj: { [key: string]: any }, fields: string[]): any {
|
||||||
|
const ret = { ...obj };
|
||||||
|
for (const f of fields) {
|
||||||
|
if (f in ret && typeof ret[f] === 'string') {
|
||||||
|
ret[f] = moment(ret[f]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function base64FromFile(file: File): Promise<string | undefined> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = () => {
|
||||||
|
const str = reader.result;
|
||||||
|
if (typeof str === 'string') {
|
||||||
|
resolve(str);
|
||||||
|
}
|
||||||
|
resolve(undefined);
|
||||||
|
}
|
||||||
|
reader.onerror = () => {
|
||||||
|
resolve(undefined);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const normFile = (e: any) => {
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return e?.fileList;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function randNum(a: number, b: number) {
|
||||||
|
const ret = a * Math.random() + b;
|
||||||
|
return parseFloat(ret.toFixed(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getParameter(param: string) {
|
||||||
|
var query = window.location.hash;
|
||||||
|
var iLen = param.length;
|
||||||
|
var iStart = query.indexOf(param);
|
||||||
|
if (iStart == -1)
|
||||||
|
return "";
|
||||||
|
iStart += iLen + 1;
|
||||||
|
var iEnd = query.indexOf("&", iStart);
|
||||||
|
if (iEnd == -1)
|
||||||
|
return query.substring(iStart);
|
||||||
|
|
||||||
|
return query.substring(iStart, iEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toFixed3(val: any) {
|
||||||
|
if (typeof val !== 'number') {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
return val.toFixed(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renTm(val: any) {
|
||||||
|
if (!val) {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
return moment(val).format('MM-DD HH:mm:ss')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dpContainerTransform(containerWidth: number, containerHeight: number, size?: DOMRect): undefined | string {
|
||||||
|
if (!size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sx = size.width / containerWidth;
|
||||||
|
const sy = size.height / containerHeight;
|
||||||
|
|
||||||
|
const scale = sx < sy ? sx : sy;
|
||||||
|
if (!scale) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const offx = containerWidth * (sx - scale) * 0.5;
|
||||||
|
const offy = containerHeight * (sy - scale) * 0.5;
|
||||||
|
|
||||||
|
return `scale(${scale.toFixed(3)}) translate(${offx.toFixed(3)}px, ${offy.toFixed(3)}px)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
|
||||||
|
if (value === null || value === undefined) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function getBasePath(): string {
|
||||||
|
if (process.env.PUBLIC_URL.startsWith('http')) {
|
||||||
|
return process.env.PUBLIC_URL;
|
||||||
|
} else {
|
||||||
|
return window.location.origin + process.env.PUBLIC_URL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { Navigate, useRoutes } from 'react-router';
|
||||||
|
import DashboardLayout from '../components/DashboardLayout';
|
||||||
|
import { Dispatch } from '../models/store';
|
||||||
|
import NotFound from './NotFound';
|
||||||
|
import ResPage from './Eng/Res';
|
||||||
|
import DamPage from './Eng/Dam';
|
||||||
|
import RivlPage from './Eng/Rivl';
|
||||||
|
import ElStPage from './Eng/ElSt';
|
||||||
|
import GatePage from './Eng/Gate';
|
||||||
|
import WfmPage from './Eng/Wfm';
|
||||||
|
import StPage from './Base/St/Manage';
|
||||||
|
import AdPage from './Base/Ad';
|
||||||
|
import DepartmentPage from './Base/Department';
|
||||||
|
import PersonelPage from './Base/Personel';
|
||||||
|
import RzHisPage from './Base/RzHis';
|
||||||
|
import UnitPage from './Base/Unit';
|
||||||
|
import VideoPage from './Base/Video';
|
||||||
|
import DataHisPage from './Serv/DataHis';
|
||||||
|
import RegPage from './Serv/Reg';
|
||||||
|
import DirPage from './Serv/Dir';
|
||||||
|
import WatchPage from './Serv/Watch';
|
||||||
|
import MgrPage from './Serv/Mgr';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const AppRouters: React.FC = () => {
|
||||||
|
const dispatch = useDispatch<Dispatch>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(window as any).__dispatch__ = dispatch;
|
||||||
|
return () => {
|
||||||
|
delete (window as any).__dispatch__;
|
||||||
|
}
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
|
||||||
|
let element = useRoutes([
|
||||||
|
{ path: '/', element: <Navigate to="/mgr/res" /> },
|
||||||
|
{
|
||||||
|
path: '/mgr', element: <DashboardLayout />, children: [
|
||||||
|
{ path: 'res', element: <ResPage /> },
|
||||||
|
{ path: 'rivl', element: <RivlPage /> },
|
||||||
|
{ path: 'dam', element: <DamPage /> },
|
||||||
|
{ path: 'elst', element: <ElStPage /> },
|
||||||
|
{ path: 'gate', element: <GatePage /> },
|
||||||
|
{ path: 'wfm', element: <WfmPage /> },
|
||||||
|
{ path: 'st', element: <StPage /> },
|
||||||
|
{ path: 'video', element: <VideoPage /> },
|
||||||
|
//{ path: 'rzhis', element: <RzHisPage /> },
|
||||||
|
{ path: 'unit', element: <UnitPage /> },
|
||||||
|
{ path: 'ad', element: <AdPage /> },
|
||||||
|
{ path: 'personel', element: <PersonelPage /> },
|
||||||
|
{ path: 'department', element: <DepartmentPage /> },
|
||||||
|
{ path: 'dir', element: <DirPage /> },
|
||||||
|
{ path: 'servreg', element: <RegPage /> },
|
||||||
|
{ path: 'servwatch', element: <WatchPage /> },
|
||||||
|
{ path: 'datahis', element: <DataHisPage /> },
|
||||||
|
{ path: 'servmgr', element: <MgrPage /> },
|
||||||
|
{ path: '*', element: <NotFound /> },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ path: '*', element: <NotFound /> },
|
||||||
|
]);
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppRouters
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { Divider, Space, Table } from 'antd';
|
||||||
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
|
import moment from 'moment';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import OpButton, { DelOpButton, EditOpButton } from '../../../components/crud/OpButton';
|
||||||
|
import { ElStItem } from '../../../models/_/defs';
|
||||||
|
import { IContextProp } from './_';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const DataTable: React.FC<{
|
||||||
|
ctx: IContextProp
|
||||||
|
}> = ({ ctx }) => {
|
||||||
|
|
||||||
|
const { pager, crud } = ctx;
|
||||||
|
const columns = useMemo<ColumnsType<ElStItem>>(() => [
|
||||||
|
{ title: '序号', key: '序号', align: 'center', width: 80, render: (_1: any, _2: any, index: number) => ctx.pager.noRender(index) },
|
||||||
|
{ title: '行政区划编码', key: '行政区划编码', dataIndex: '行政区划编码', width: 120, align: 'center' },
|
||||||
|
{ title: '行政区划名称', key: '行政区划名称', dataIndex: '行政区划名称', width: 120, align: 'center' },
|
||||||
|
|
||||||
|
{ title: '创建时间', key: '创建时间', dataIndex: '创建时间', width: 120, align: 'center' },
|
||||||
|
{
|
||||||
|
title: '操作', key: 'op', align: 'center', width: 200, render: rec =>
|
||||||
|
(<Space split={<Divider type="vertical" />}>
|
||||||
|
<OpButton text='编辑'></OpButton>
|
||||||
|
<OpButton text='详情'></OpButton>
|
||||||
|
<OpButton text='删除'></OpButton>
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
], []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
rowKey={row => row.水电站代码}
|
||||||
|
{...pager.tableProps}
|
||||||
|
dataSource={[]}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataTable
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { SearchOutlined } from '@ant-design/icons'
|
||||||
|
import { Button, Col, Form, Input, Row, Select, Space } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
import { IContextProp } from './_'
|
||||||
|
|
||||||
|
const SearchBar: React.FC<{
|
||||||
|
ctx: IContextProp
|
||||||
|
}> = ({ ctx }) => {
|
||||||
|
const { pager } = ctx;
|
||||||
|
|
||||||
|
const doSearch = (data: any) => {
|
||||||
|
pager.search({
|
||||||
|
search: {
|
||||||
|
username: data.name,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form layout="horizontal" onFinish={doSearch}>
|
||||||
|
<Row gutter={32}>
|
||||||
|
<Col flex={0} style={{ width: 260 }}>
|
||||||
|
<Form.Item name="行政区划编码" label="行政区划编码">
|
||||||
|
<Input placeholder='请输入行政区划编码' allowClear />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col flex={0} style={{ width: 260 }}>
|
||||||
|
<Form.Item name="行政区划名称" label="行政区划名称">
|
||||||
|
<Input placeholder='请输入行政区划名称' allowClear />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col>
|
||||||
|
<Space>
|
||||||
|
<Button htmlType='submit' icon={<SearchOutlined />} type="primary">
|
||||||
|
查询
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => ctx.crud.goto('add', {})}>
|
||||||
|
新增
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchBar
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { message } from "antd";
|
||||||
|
import useCrud, { BasicCrudType, CrudContext } from "../../../components/crud/useCrud";
|
||||||
|
import usePageTable, { PageTableContext } from "../../../components/crud/usePageTable";
|
||||||
|
import { ElStItem } from "../../../models/_/defs";
|
||||||
|
import { httpGet, httpPost, paginate, paginateDemo } from "../../../utils/request";
|
||||||
|
|
||||||
|
|
||||||
|
type CRUD_TYPER = BasicCrudType;
|
||||||
|
|
||||||
|
|
||||||
|
export type IContextProp = {
|
||||||
|
pager: PageTableContext<ElStItem>;
|
||||||
|
crud: CrudContext<CRUD_TYPER>;
|
||||||
|
saveOrUpdate: (val: FormPropType) => Promise<void>;
|
||||||
|
del: (val: any) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type FormPropType = {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function usePageContext(): IContextProp {
|
||||||
|
const crud = useCrud<CRUD_TYPER>();
|
||||||
|
const pager = usePageTable<ElStItem>((opt: any) => paginateDemo('xzqh.json', opt));
|
||||||
|
|
||||||
|
|
||||||
|
const saveOrUpdate = async (val: FormPropType): Promise<void> => {
|
||||||
|
|
||||||
|
if (crud.loading || (crud.mode !== 'add' && crud.mode !== 'edit')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
crud.setLoading(true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
const res: any = await httpPost(apiPaths.whitelist[crud.mode], val);
|
||||||
|
if (res) {
|
||||||
|
message.success('操作成功');
|
||||||
|
pager.refresh();
|
||||||
|
crud.goto(null, null);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
crud.setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const del = async (val: any) => {
|
||||||
|
|
||||||
|
crud.setLoading(true);
|
||||||
|
/*
|
||||||
|
const res: any = await httpGet(`${apiPaths.whitelist.del}/${val?.userId}`);
|
||||||
|
if (res) {
|
||||||
|
message.success('操作成功');
|
||||||
|
pager.refresh();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
crud.setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
crud,
|
||||||
|
pager,
|
||||||
|
saveOrUpdate,
|
||||||
|
del
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Modal } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
import DataTable from './DataTable'
|
||||||
|
import SearchBar from './SearchBar'
|
||||||
|
import { usePageContext } from './_'
|
||||||
|
|
||||||
|
const SysUserPage: React.FC = () => {
|
||||||
|
|
||||||
|
const ctx = usePageContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='content-body common-style'>
|
||||||
|
<SearchBar ctx={ctx} />
|
||||||
|
<DataTable ctx={ctx} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SysUserPage
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { Space, Table } from 'antd';
|
||||||
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
|
import moment from 'moment';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import OpButton, { DelOpButton, EditOpButton } from '../../../components/crud/OpButton';
|
||||||
|
import { DepartmentItem } from '../../../models/_/defs';
|
||||||
|
import { IContextProp } from './_';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const DataTable: React.FC<{
|
||||||
|
ctx: IContextProp
|
||||||
|
}> = ({ ctx }) => {
|
||||||
|
|
||||||
|
const { pager, crud } = ctx;
|
||||||
|
const columns = useMemo<ColumnsType<DepartmentItem>>(() => [
|
||||||
|
{ title: '序号', key: 'id', width: 80, align: 'center', render: (_1: any, _2: any, index: number) => ctx.pager.noRender(index) },
|
||||||
|
{ title: '部门编码', key: '部门编码', dataIndex: '部门编码', width: 120, align: 'center' },
|
||||||
|
{ title: '部门名称', key: '部门名称', dataIndex: '部门名称', width: 120, align: 'center' },
|
||||||
|
{ title: '上级部门', key: '上级部门', dataIndex: '上级部门', width: 120, align: 'center' },
|
||||||
|
{ title: '排序号', key: '排序号', dataIndex: '排序号', width: 120, align: 'center' },
|
||||||
|
{ title: '备注', key: '备注', dataIndex: '备注', width: 200, align: 'center' },
|
||||||
|
{ title: '创建日期', key: '创建日期', dataIndex: '创建日期', width: 120, align: 'center' },
|
||||||
|
{
|
||||||
|
title: '操作', key: 'op', align: 'center', width: 200, render: rec =>
|
||||||
|
(<Space>
|
||||||
|
<OpButton text='编辑'></OpButton>
|
||||||
|
<OpButton text='详情'></OpButton>
|
||||||
|
<OpButton text='权限'></OpButton>
|
||||||
|
<OpButton text='删除'></OpButton>
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
], []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
rowKey={row => row.部门编码}
|
||||||
|
{...pager.tableProps}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataTable
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { SearchOutlined } from '@ant-design/icons'
|
||||||
|
import { Button, Col, Form, Input, Row, Space } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
import { IContextProp } from './_'
|
||||||
|
|
||||||
|
const SearchBar: React.FC<{
|
||||||
|
ctx: IContextProp
|
||||||
|
}> = ({ ctx }) => {
|
||||||
|
const { pager } = ctx;
|
||||||
|
|
||||||
|
const doSearch = (data: any) => {
|
||||||
|
pager.search({
|
||||||
|
search: {
|
||||||
|
username: data.name,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form layout="horizontal" onFinish={doSearch}>
|
||||||
|
<Row gutter={32}>
|
||||||
|
<Col flex={0} style={{ width: 260 }}>
|
||||||
|
<Form.Item name="部门编码" label="部门编码">
|
||||||
|
<Input placeholder='请输入部门编码' allowClear />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col flex={0} style={{ width: 260 }}>
|
||||||
|
<Form.Item name="部门名称" label="部门名称">
|
||||||
|
<Input placeholder='请输入部门名称' allowClear />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col>
|
||||||
|
<Space>
|
||||||
|
<Button htmlType='submit' icon={<SearchOutlined />} type="primary">
|
||||||
|
查询
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => ctx.crud.goto('add', {})}>
|
||||||
|
新增
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchBar
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { message } from "antd";
|
||||||
|
import useCrud, { BasicCrudType, CrudContext } from "../../../components/crud/useCrud";
|
||||||
|
import usePageTable, { PageTableContext } from "../../../components/crud/usePageTable";
|
||||||
|
import { DepartmentItem } from "../../../models/_/defs";
|
||||||
|
import { httpGet, httpPost, paginate, paginateDemo } from "../../../utils/request";
|
||||||
|
|
||||||
|
|
||||||
|
type CRUD_TYPER = BasicCrudType;
|
||||||
|
|
||||||
|
|
||||||
|
export type IContextProp = {
|
||||||
|
pager: PageTableContext<DepartmentItem>;
|
||||||
|
crud: CrudContext<CRUD_TYPER>;
|
||||||
|
saveOrUpdate: (val: FormPropType) => Promise<void>;
|
||||||
|
del: (val: any) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type FormPropType = {
|
||||||
|
userId: number;
|
||||||
|
acl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function usePageContext(): IContextProp {
|
||||||
|
const crud = useCrud<CRUD_TYPER>();
|
||||||
|
const pager = usePageTable<DepartmentItem>((opt: any) => paginateDemo('department.json', opt));
|
||||||
|
|
||||||
|
|
||||||
|
const saveOrUpdate = async (val: FormPropType): Promise<void> => {
|
||||||
|
|
||||||
|
if (crud.loading || (crud.mode !== 'add' && crud.mode !== 'edit')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
crud.setLoading(true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
const res: any = await httpPost(apiPaths.whitelist[crud.mode], val);
|
||||||
|
if (res) {
|
||||||
|
message.success('操作成功');
|
||||||
|
pager.refresh();
|
||||||
|
crud.goto(null, null);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
crud.setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const del = async (val: any) => {
|
||||||
|
|
||||||
|
crud.setLoading(true);
|
||||||
|
/*
|
||||||
|
const res: any = await httpGet(`${apiPaths.whitelist.del}/${val?.userId}`);
|
||||||
|
if (res) {
|
||||||
|
message.success('操作成功');
|
||||||
|
pager.refresh();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
crud.setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
crud,
|
||||||
|
pager,
|
||||||
|
saveOrUpdate,
|
||||||
|
del
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Modal } from 'antd'
|
||||||
|
import React from 'react'
|
||||||
|
import DataTable from './DataTable'
|
||||||
|
import SearchBar from './SearchBar'
|
||||||
|
import { usePageContext } from './_'
|
||||||
|
|
||||||
|
const DepartmentPage: React.FC = () => {
|
||||||
|
|
||||||
|
const ctx = usePageContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='content-body common-style'>
|
||||||
|
<SearchBar ctx={ctx} />
|
||||||
|
<DataTable ctx={ctx} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DepartmentPage
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { Divider, Space, Table } from 'antd';
|
||||||
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
|
import moment from 'moment';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import OpButton, { DelOpButton, EditOpButton } from '../../../components/crud/OpButton';
|
||||||
|
import { SysUserItem } from '../../../models/_/defs';
|
||||||
|
import { IContextProp } from './_';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const DataTable: React.FC<{
|
||||||
|
ctx: IContextProp
|
||||||
|
}> = ({ ctx }) => {
|
||||||
|
|
||||||
|
const { pager, crud } = ctx;
|
||||||
|
const columns = useMemo<ColumnsType<SysUserItem>>(() => [
|
||||||
|
{ title: '序号', key: 'id', align: 'center', width: 80, render: (_1: any, _2: any, index: number) => ctx.pager.noRender(index) },
|
||||||
|
{ title: '用户帐号', key: '用户帐号', dataIndex: '用户帐号', width: 120, align: 'center' },
|
||||||
|
{ title: '用户姓名', key: '用户姓名', dataIndex: '用户姓名', width: 120, align: 'center' },
|
||||||
|
{ title: '手机号码', key: '手机号码', dataIndex: '手机号码', width: 120, align: 'center' },
|
||||||
|
{ title: '部门', key: '部门', dataIndex: '部门', width: 120, align: 'center' },
|
||||||
|
{ title: '职务', key: '职务', dataIndex: '职务', width: 120, align: 'center' },
|
||||||
|
{ title: '状态', key: '状态', dataIndex: '状态', width: 120, align: 'center' },
|
||||||
|
{
|
||||||
|
title: '操作', key: 'op', align: 'center', width: 200, render: rec =>
|
||||||
|
(<Space split={<Divider type="vertical" />}>
|
||||||
|
<OpButton text='编辑'></OpButton>
|
||||||
|
<OpButton text='详情'></OpButton>
|
||||||
|
<OpButton text='密码'></OpButton>
|
||||||
|
<OpButton text='删除'></OpButton>
|
||||||
|
<OpButton text='冻结'></OpButton>
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
], []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
rowKey={row => row.用户账号}
|
||||||
|
{...pager.tableProps}
|
||||||
|
dataSource={[]}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataTable
|
||||||