diff --git a/api/app/lib/middlewares/proxy.js b/api/app/lib/middlewares/proxy.js new file mode 100644 index 0000000..657c87c --- /dev/null +++ b/api/app/lib/middlewares/proxy.js @@ -0,0 +1,12 @@ +const proxy = require('koa-proxy'); + +module.exports.entry = function (app, router, opts) { + app.use(proxy({ + host: opts.host, + match: opts.match, + map: function (path) { + return path.replace(opts.match, ''); + }, + suppressRequestHeaders: ['content-length'], + })) +} \ No newline at end of file diff --git a/api/config.js b/api/config.js index 40af44e..0a2ce22 100644 --- a/api/config.js +++ b/api/config.js @@ -78,6 +78,18 @@ const product = { dbConfig: DATABASE_CONFIG, } + }, { + entry: require('./app/lib/middlewares/proxy').entry, + opts: { + host: 'https://waterconservancy.anxinyun.cn', + match: /\/_water\//, + } + }, { + entry: require('./app/lib/middlewares/proxy').entry, + opts: { + host: 'https://smartworksafety.anxinyun.cn', + match: /\/_worksafety\//, + } } ], dc: { diff --git a/api/package.json b/api/package.json index e4d46c6..daae907 100644 --- a/api/package.json +++ b/api/package.json @@ -27,7 +27,7 @@ "jszip": "^3.10.1", "kafka-node": "^2.2.3", "koa-convert": "^1.2.0", - "koa-proxy": "^0.9.0", + "koa-proxy": "^1.0.0-alpha.3", "moment": "^2.24.0", "mqtt": "^4.3.7", "node-schedule": "^2.1.0", diff --git a/super-screen/client/assets/images/weather/baoxue.png b/super-screen/client/assets/images/weather/baoxue.png new file mode 100644 index 0000000..c6791b4 Binary files /dev/null and b/super-screen/client/assets/images/weather/baoxue.png differ diff --git a/super-screen/client/assets/images/weather/dayu.png b/super-screen/client/assets/images/weather/dayu.png new file mode 100644 index 0000000..3c7aef6 Binary files /dev/null and b/super-screen/client/assets/images/weather/dayu.png differ diff --git a/super-screen/client/assets/images/weather/duoyun.png b/super-screen/client/assets/images/weather/duoyun.png new file mode 100644 index 0000000..1d0dc7d Binary files /dev/null and b/super-screen/client/assets/images/weather/duoyun.png differ diff --git a/super-screen/client/assets/images/weather/fucheng.png b/super-screen/client/assets/images/weather/fucheng.png new file mode 100644 index 0000000..b614c9a Binary files /dev/null and b/super-screen/client/assets/images/weather/fucheng.png differ diff --git a/super-screen/client/assets/images/weather/leizhengyu.png b/super-screen/client/assets/images/weather/leizhengyu.png new file mode 100644 index 0000000..d1818ca Binary files /dev/null and b/super-screen/client/assets/images/weather/leizhengyu.png differ diff --git a/super-screen/client/assets/images/weather/mai.png b/super-screen/client/assets/images/weather/mai.png new file mode 100644 index 0000000..ebb2a4b Binary files /dev/null and b/super-screen/client/assets/images/weather/mai.png differ diff --git a/super-screen/client/assets/images/weather/qiangshachengbao.png b/super-screen/client/assets/images/weather/qiangshachengbao.png new file mode 100644 index 0000000..19e76e4 Binary files /dev/null and b/super-screen/client/assets/images/weather/qiangshachengbao.png differ diff --git a/super-screen/client/assets/images/weather/qing.png b/super-screen/client/assets/images/weather/qing.png new file mode 100644 index 0000000..0ec8841 Binary files /dev/null and b/super-screen/client/assets/images/weather/qing.png differ diff --git a/super-screen/client/assets/images/weather/shachengbao.png b/super-screen/client/assets/images/weather/shachengbao.png new file mode 100644 index 0000000..ea3f6d6 Binary files /dev/null and b/super-screen/client/assets/images/weather/shachengbao.png differ diff --git a/super-screen/client/assets/images/weather/tedabaoyu.png b/super-screen/client/assets/images/weather/tedabaoyu.png new file mode 100644 index 0000000..a6737e2 Binary files /dev/null and b/super-screen/client/assets/images/weather/tedabaoyu.png differ diff --git a/super-screen/client/assets/images/weather/weizhi.png b/super-screen/client/assets/images/weather/weizhi.png new file mode 100644 index 0000000..9ae6864 Binary files /dev/null and b/super-screen/client/assets/images/weather/weizhi.png differ diff --git a/super-screen/client/assets/images/weather/wu.png b/super-screen/client/assets/images/weather/wu.png new file mode 100644 index 0000000..fe7b233 Binary files /dev/null and b/super-screen/client/assets/images/weather/wu.png differ diff --git a/super-screen/client/assets/images/weather/xiaoxue.png b/super-screen/client/assets/images/weather/xiaoxue.png new file mode 100644 index 0000000..53800ff Binary files /dev/null and b/super-screen/client/assets/images/weather/xiaoxue.png differ diff --git a/super-screen/client/assets/images/weather/xiaoyu.png b/super-screen/client/assets/images/weather/xiaoyu.png new file mode 100644 index 0000000..3d2f22a Binary files /dev/null and b/super-screen/client/assets/images/weather/xiaoyu.png differ diff --git a/super-screen/client/assets/images/weather/yangsha.png b/super-screen/client/assets/images/weather/yangsha.png new file mode 100644 index 0000000..b8ae80c Binary files /dev/null and b/super-screen/client/assets/images/weather/yangsha.png differ diff --git a/super-screen/client/assets/images/weather/yingtian.png b/super-screen/client/assets/images/weather/yingtian.png new file mode 100644 index 0000000..61c5876 Binary files /dev/null and b/super-screen/client/assets/images/weather/yingtian.png differ diff --git a/super-screen/client/assets/images/weather/yujiaxue.png b/super-screen/client/assets/images/weather/yujiaxue.png new file mode 100644 index 0000000..a3e049e Binary files /dev/null and b/super-screen/client/assets/images/weather/yujiaxue.png differ diff --git a/super-screen/client/assets/images/weather/zhenyu.png b/super-screen/client/assets/images/weather/zhenyu.png new file mode 100644 index 0000000..36de494 Binary files /dev/null and b/super-screen/client/assets/images/weather/zhenyu.png differ diff --git a/super-screen/client/assets/images/weather/zhongxue.png b/super-screen/client/assets/images/weather/zhongxue.png new file mode 100644 index 0000000..0e713e8 Binary files /dev/null and b/super-screen/client/assets/images/weather/zhongxue.png differ diff --git a/super-screen/client/assets/images/weather/zhongyu.png b/super-screen/client/assets/images/weather/zhongyu.png new file mode 100644 index 0000000..3309cd0 Binary files /dev/null and b/super-screen/client/assets/images/weather/zhongyu.png differ diff --git a/super-screen/client/src/sections/community-safty/components/style.less b/super-screen/client/src/sections/community-safty/components/style.less index 9763184..637e9ba 100644 --- a/super-screen/client/src/sections/community-safty/components/style.less +++ b/super-screen/client/src/sections/community-safty/components/style.less @@ -456,7 +456,7 @@ background-size: 100% 100%; margin-bottom: 7px; padding-left: 7px; - padding-right: 16px; + padding-right: 6px; .alarm_title1 { float: left; @@ -466,6 +466,10 @@ font-size: 16px; color: #FFFFFF; letter-spacing: 0; + width: 75%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .alarm_title2 { diff --git a/super-screen/client/src/sections/water-prevention/actions/index.js b/super-screen/client/src/sections/water-prevention/actions/index.js index 444af37..9d6fc3e 100644 --- a/super-screen/client/src/sections/water-prevention/actions/index.js +++ b/super-screen/client/src/sections/water-prevention/actions/index.js @@ -1,7 +1,11 @@ 'use strict'; import * as example from './example' +import * as waterconservancy from './waterconservancy' +import * as worksafety from './worksafety' export default { ...example, + ...waterconservancy, + ...worksafety, } \ No newline at end of file diff --git a/super-screen/client/src/sections/water-prevention/actions/waterconservancy.js b/super-screen/client/src/sections/water-prevention/actions/waterconservancy.js new file mode 100644 index 0000000..29f8dd2 --- /dev/null +++ b/super-screen/client/src/sections/water-prevention/actions/waterconservancy.js @@ -0,0 +1,83 @@ +'use strict'; + +import { basicAction } from '@peace/utils' +import { ApiTable } from '$utils' + +export function getPcode(orgId) { + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + actionType: 'GET_PCODE', + url: `${ApiTable.getPcode}`, + msg: { error: '获取水环境pcode失败' }, + }); +} + +export function waterLogin(data) { + return dispatch => basicAction({ + type: 'post', + data, + dispatch: dispatch, + actionType: 'POST_WATER_LOGIN', + url: `${ApiTable.waterLogin}`, + msg: { error: '登录水环境失败' }, + // reducer: { name: 'members' } + }); +} + +export function getWaterLevelTrend(query) { + return dispatch => basicAction({ + type: 'get', + query, + dispatch: dispatch, + actionType: 'GET_WATER_LEVEL_TREND', + url: `${ApiTable.getWaterLevelTrend}`, + msg: { error: '获取水位趋势失败' }, + }); +} + +export function getWaterStructures(query) { + return dispatch => basicAction({ + type: 'get', + query, + dispatch: dispatch, + actionType: 'GET_WATER_STRUCTURES', + url: `${ApiTable.getWaterStructures}`, + msg: { error: '获取水务结构物失败' }, + }); +} + +export function getWaterVideoList(struIds, query) { + return dispatch => basicAction({ + type: 'get', + query, + dispatch: dispatch, + actionType: 'GET_WATER_VIDEO_LIST', + url: `${ApiTable.getWaterVideoList.replace('{struIds}', struIds)}`, + msg: { error: '获取水务视频失败' }, + }); +} + +export function getYsAccessToken(query) { + return dispatch => basicAction({ + type: 'get', + query, + dispatch: dispatch, + actionType: 'GET_YS_ACCESS_TOKEN', + url: `${ApiTable.getYsAccessToken}`, + msg: { error: '获取萤石授权失败' }, + }); +} + +export function getWaterAlarms(query) { + return dispatch => basicAction({ + type: 'post', + query, + // data: { "keywords": "", "status": "new", "levels": [1, 2, 3], "orderBy": "endTime", "orderDirection": "desc", "limit": 10, "offset": 0, "structures": null, "types": [1] }, + data: { "structures": null, "orderBy": "endTime", "endTime": "2022-06-30 23:59:59", "keywords": "", "levels": [1, 2, 3], "startTime": "2022-06-01 00:00:00", "orderDirection": "desc", "status": "history", "offset": 0, "types": [1], "limit": 10 }, + dispatch: dispatch, + actionType: 'GET_WATER_ALARMS', + url: `${ApiTable.getWaterAlarms}`, + msg: { error: '获取告警失败' }, + }); +} diff --git a/super-screen/client/src/sections/water-prevention/actions/worksafety.js b/super-screen/client/src/sections/water-prevention/actions/worksafety.js new file mode 100644 index 0000000..89d6db0 --- /dev/null +++ b/super-screen/client/src/sections/water-prevention/actions/worksafety.js @@ -0,0 +1,70 @@ +'use strict'; + +import { basicAction } from '@peace/utils' +import { ApiTable } from '$utils' + +export function worksafetyLogin(data) { + return dispatch => basicAction({ + type: 'post', + data, + dispatch: dispatch, + actionType: 'POST_WORKSAFETY_LOGIN', + url: `${ApiTable.worksafetyLogin}`, + msg: { error: '登录安监失败' }, + }); +} + +export function getEmerOrgList(query) { + return dispatch => basicAction({ + type: 'get', + query, + dispatch: dispatch, + actionType: 'GET_EMER_ORG_LIST', + url: `${ApiTable.getEmerOrgList}`, + msg: { error: '登录应急机构失败' }, + }); +} + +export function getEmerTeamList(query) { + return dispatch => basicAction({ + type: 'get', + query, + dispatch: dispatch, + actionType: 'GET_EMER_TEAM_LIST', + url: `${ApiTable.getEmerTeamList}`, + msg: { error: '登录应急队伍失败' }, + }); +} + +export function getEmerExpertList(query) { + return dispatch => basicAction({ + type: 'get', + query, + dispatch: dispatch, + actionType: 'GET_EMER_EXPERT_LIST', + url: `${ApiTable.getEmerExpertList}`, + msg: { error: '登录应急专家失败' }, + }); +} + +export function getEmerMedicalList(query) { + return dispatch => basicAction({ + type: 'get', + query, + dispatch: dispatch, + actionType: 'GET_EMER_MEDICAL_LIST', + url: `${ApiTable.getEmerMedicalList}`, + msg: { error: '登录医疗机构失败' }, + }); +} + +export function getEmerRefugeList(query) { + return dispatch => basicAction({ + type: 'get', + query, + dispatch: dispatch, + actionType: 'GET_EMER_REFUGE_LIST', + url: `${ApiTable.getEmerRefugeList}`, + msg: { error: '登录避难场所失败' }, + }); +} \ No newline at end of file diff --git a/super-screen/client/src/sections/water-prevention/components/emergency-left-bottom.js b/super-screen/client/src/sections/water-prevention/components/emergency-left-bottom.js index b5eea29..a177073 100644 --- a/super-screen/client/src/sections/water-prevention/components/emergency-left-bottom.js +++ b/super-screen/client/src/sections/water-prevention/components/emergency-left-bottom.js @@ -1,55 +1,42 @@ import React from 'react' import { Box } from '$components'; +import { Tooltip } from 'antd'; function Infrastructure(props) { - + const { waterLevelAlarms } = props; return
-
-
-
-
-
2023-06-20 17:00:00
-
二级预警
-
-
萌萌小区25栋305等等等等等发生等等火灾扥大哥大呢个
-
-
-
-
-
-
-
2023-06-20 17:00:00
-
一级预警
-
-
萌萌小区25栋305等等等等等发生等等火灾扥大哥大呢个
-
-
-
-
-
-
-
2023-06-20 17:00:00
-
二级预警
+ { + // alarm_unhandle FF2C2C alarm_handle FFEA00 + waterLevelAlarms.map(a =>
= 3 ? 'alarm_handle' : 'alarm_unhandle'}> +
+
+
+ +
{a.alarms[0]?.source?.name}
+
+
= 3 ? '#FFEA00' : '#FF2C2C' }}>{convertLevelToLabel(a.alarms[0]?.level)}
+
+
{a.alarms[0]?.content}
-
萌萌小区25栋305等等等等等发生等等火灾扥大哥大呢个
-
-
-
-
-
-
-
2023-06-20 17:00:00
-
二级预警
-
-
萌萌小区25栋305等等等等等发生等等火灾扥大哥大呢个
-
-
+
) + }
} export default Infrastructure; - +function convertLevelToLabel(level) { + switch (level) { + case 1: + return '一级预警'; + case 2: + return '二级预警'; + case 3: + return '三级预警'; + default: + return ''; + } +} diff --git a/super-screen/client/src/sections/water-prevention/components/emergency-right-top.js b/super-screen/client/src/sections/water-prevention/components/emergency-right-top.js index fd9260a..e676ba4 100644 --- a/super-screen/client/src/sections/water-prevention/components/emergency-right-top.js +++ b/super-screen/client/src/sections/water-prevention/components/emergency-right-top.js @@ -3,7 +3,7 @@ import { Box } from '$components'; import './style.less'; function CitySafty(props) { - const { emengencyTab } = props; + const { emengencyTab, emerResource } = props; const tab_name = { yjjg: '应急机构', yjdw: '应急队伍', @@ -11,29 +11,28 @@ function CitySafty(props) { yljg: '医疗机构', bncs: '避难场所', } - return + return
{ - [1, 2, 3, 4, 5, 6, 7].map((s, index) => { + emerResource?.map((s, index) => { return
场所名称
-
{tab_name[emengencyTab] + s}
+
{s.name || '--'}
场所地点
-
南昌县中心街道人民路168号 - 人民路168号人民路168号
+
{s.address || s.region || '--'}
承载人数
-
2000 人
+
-- 人
目标距离
-
2.5 Km
+
-- Km
场所{index + 1}
diff --git a/super-screen/client/src/sections/water-prevention/components/left-bottom.js b/super-screen/client/src/sections/water-prevention/components/left-bottom.js index 0f3f86f..db6523b 100644 --- a/super-screen/client/src/sections/water-prevention/components/left-bottom.js +++ b/super-screen/client/src/sections/water-prevention/components/left-bottom.js @@ -1,8 +1,10 @@ import React from 'react' import { Box } from '$components'; -import RingChart from './charts/ring' -function LeftBottom(props) { +import RingChart from './charts/ring'; +import { SHUI_ZHAN } from '../constants/water'; +function LeftBottom(props) { + const { waterLevelLength } = props; const colors = [ { linearGradientTo: 'rgba(5, 207, 247, 0)', @@ -31,9 +33,9 @@ function LeftBottom(props) { ]; const data = [ - { name: '泵站站点', value: 2560 }, - { name: '雨量站点', value: 1560 }, - { name: '水位站点', value: 3560 } + { name: '泵站站点', value: SHUI_ZHAN.length }, + { name: '雨量站点', value: 1 }, + { name: '水位站点', value: waterLevelLength } ] return diff --git a/super-screen/client/src/sections/water-prevention/components/right-bottom.js b/super-screen/client/src/sections/water-prevention/components/right-bottom.js index 904aafd..f3390ae 100644 --- a/super-screen/client/src/sections/water-prevention/components/right-bottom.js +++ b/super-screen/client/src/sections/water-prevention/components/right-bottom.js @@ -1,31 +1,53 @@ import React, { useEffect, useState } from 'react' import { Box } from '$components'; +import { Select } from 'antd'; import './style.less'; +const { Option } = Select; function DataTop5(props) { + const { structures, accessToken, videoList } = props; + const [showVideoList, setShowVideoList] = useState([]); + useEffect(() => { + if (videoList.length && structures.length) setShowVideoList(videoList.filter(v => v.structId === structures[0].id)); + }, [videoList]) - return + const renderSubtitle = () => ( + + ); + return
-
-
南昌大道14:12:32
-
-
-
南昌大道11:34:12
-
-
-
南昌大道08:34:12
-
-
-
南昌大道09:12:34
-
-
-
南昌大道08:34:12
-
-
-
南昌大道09:12:34
-
+ { + showVideoList?.map((v, i) => { + const src = `https://open.ys7.com/ezopen/h5/iframe?url=${v.url}&autoplay=1&accessToken=${accessToken}` + return
+ +
+ {v.name} +
+
+ }) + } +
} diff --git a/super-screen/client/src/sections/water-prevention/components/right-top.js b/super-screen/client/src/sections/water-prevention/components/right-top.js index a9073d7..d59d52d 100644 --- a/super-screen/client/src/sections/water-prevention/components/right-top.js +++ b/super-screen/client/src/sections/water-prevention/components/right-top.js @@ -1,8 +1,10 @@ import React, { useEffect, useState } from 'react' import { Box } from '$components'; +import { Tooltip } from 'antd'; import './style.less'; function CitySafty(props) { + const { trendData } = props; return
@@ -14,16 +16,16 @@ function CitySafty(props) {
- {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map((s, index) => { - return
+ {trendData?.map((s, index) => { + return
- 点位{index + 1} + {s.name}
-
2023-01-02 12:12:12
-
111
+
{s.currentTime}
+
{s.waterLevel}{s.waterLevel &&
0 ? '_upicon' : '_downicon'} />}
-
- {index % 2 == 0 ? '正常' : '告警'} +
+ {s.futureState === '正常' ? '正常' : s.futureState.length ? '告警' : '--'}
diff --git a/super-screen/client/src/sections/water-prevention/components/style.less b/super-screen/client/src/sections/water-prevention/components/style.less index 42e567a..84df1b1 100644 --- a/super-screen/client/src/sections/water-prevention/components/style.less +++ b/super-screen/client/src/sections/water-prevention/components/style.less @@ -365,11 +365,12 @@ height: 100%; display: flex; flex-wrap: wrap; - align-items: center; - justify-content: center; + align-items: start; + justify-content: start; ._item { width: 46%; + margin: 0.5% 2%; height: 32%; background: url('/assets/images/homepage/fire/videobg.png') no-repeat; background-size: 100% 100%; @@ -377,9 +378,9 @@ .video_bottom { position: absolute; - bottom: 9%; - left: 4%; - width: 91%; + bottom: 0; + left: 2%; + width: 96%; height: 20px; background: rgba(0, 0, 0, 0.4); font-size: 12px; @@ -387,6 +388,12 @@ display: flex; justify-content: space-between; + .video_bottom_text { + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } } } } @@ -505,12 +512,16 @@ ._rank_item1 { text-align: center; - width: 15%; + width: 20%; + margin-left: 5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } ._rank_item2 { // text-align: center; - width: 40%; + width: 35%; } ._rank_item3 { diff --git a/super-screen/client/src/sections/water-prevention/components/weather.js b/super-screen/client/src/sections/water-prevention/components/weather.js index 4e7536e..27955a4 100644 --- a/super-screen/client/src/sections/water-prevention/components/weather.js +++ b/super-screen/client/src/sections/water-prevention/components/weather.js @@ -1,18 +1,153 @@ -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState, useMemo } from 'react' import moment from 'moment'; +import { RouteRequest } from '@peace/utils'; + +const WEATHERMARGIN = 1 * 60 * 60 * 1000; +const WEATHERArr = [ + { + lbl: '未知', + key: 'weizhi', + }, + { + lbl: '暴雪', + key: 'baoxue', + }, + { + lbl: '多云', + key: 'duoyun', + }, + { + lbl: '浮沉', + key: 'fucheng', + }, + { + lbl: '雷阵雨', + key: 'leizhengyu', + }, + { + lbl: '霾', + key: 'mai', + }, + { + lbl: '强沙尘暴', + key: 'qiangshachengbao', + }, + { + lbl: '晴', + key: 'qing', + }, + { + lbl: '沙尘暴', + key: 'shachengbao', + }, + { + lbl: '特大暴雨', + key: 'tedabaoyu', + }, + { + lbl: '雾', + key: 'wu', + }, + { + lbl: '小雪', + key: 'xiaoxue', + }, + { + lbl: '小雨', + key: 'xiaoyu', + }, + { + lbl: '扬沙', + key: 'yangsha', + }, + { + lbl: '阴天', + key: 'yingtian', + }, + { + lbl: '雨夹雪', + key: 'yujiaxue', + }, + { + lbl: '阵雨', + key: 'zhenyu', + }, + { + lbl: '中雪', + key: 'zhongxue', + }, + { + lbl: '中雨', + key: 'zhongyu', + }, + { + lbl: '大雨', + key: 'dayu', + }, +]; +const ICONSMAP = {}; + +WEATHERArr.forEach(({ lbl, key }) => { + const icon = `/assets/images/weather/${key}.png`; + ICONSMAP[lbl] = icon; +}); function Weather() { + const [time, setTime] = useState(new Date().toLocaleTimeString()); + const [weather, setWeather] = useState(''); + const [temp, setTemp] = useState(0); + + useEffect(() => { + const timeUpdate = setInterval(() => { + setTime(new Date().toLocaleTimeString()); + }, 1000); + return () => { + clearInterval(timeUpdate); + }; + }, []); + + const queryWeather = () => { + RouteRequest.get(`/query/weather?cname=南昌市`).then((doc) => { + console.log(doc, 'weather'); + const w = doc?.text || '未知'; + const temp = doc?.temp || 0; + setWeather(w); + setTemp(temp); + }); + }; + + useEffect(() => { + queryWeather(); + const timeUpdate = setInterval(() => { + queryWeather(); + }, WEATHERMARGIN); + return () => { + clearInterval(timeUpdate); + }; + }, []); + + const iconSrc = useMemo(() => { + const icon = ICONSMAP[weather]; + if (icon) return icon; + const rgx = new RegExp(weather); + for (const k in ICONSMAP) { + if (rgx.test(k)) { + return ICONSMAP[k]; + } + } + return ''; + }, [weather]); return
{moment().format('YYYY-MM-DD')}
-
{moment().format('HH:mm:ss')}
+
{time}
-
+ icon
-
晴转多云
-
18~26℃
+
{weather}
+
{temp}℃
} diff --git a/super-screen/client/src/sections/water-prevention/constants/water.js b/super-screen/client/src/sections/water-prevention/constants/water.js new file mode 100644 index 0000000..aab77db --- /dev/null +++ b/super-screen/client/src/sections/water-prevention/constants/water.js @@ -0,0 +1,22 @@ +export const SHUI_ZHAN = [ + { name: '莲塘渡槽', location: [115.95219, 28.5429711], }, + { name: '雄溪站', location: [115.908888, 28.5337351], structId: 2945 }, + { name: '塔田站', location: [115.916979, 28.5319411], }, + { name: '万寿湖站', location: [115.929167, 28.5222881], }, + { name: '张坊站', location: [115.9092, 28.5049421], }, + { name: '东山站', location: [115.887528, 28.4872531], }, + { name: '河外泵站', location: [115.889888, 28.4856311], }, + { name: '三山站', location: [115.888521, 28.4726391], }, + { name: '内湖电排站', location: [115.891311, 28.4692441], }, + { name: '沥山站', location: [115.897692, 28.5616881], }, + { name: '象湖站', location: [115.894774, 28.5789311], }, + { name: '姚塘站', location: [115.863709, 28.579954], }, + { name: '河下站', location: [115.844769, 28.5602231], }, + { name: '霞山站', location: [115.841465, 28.5508371], }, + { name: '石岐站', location: [115.838675, 28.5507611], }, + { name: '清湖站', location: [115.838418, 28.5378311], }, + { name: '虎山站', location: [115.843673, 28.5366861], }, + { name: '新八月湖站', location: [115.880322, 28.6076911], }, + // { name: '八月湖站', location: [115.868392, 28.6091981], }, + { name: '河口电排站', location: [115.924659, 28.52865] }, +] \ No newline at end of file diff --git a/super-screen/client/src/sections/water-prevention/containers/gis.js b/super-screen/client/src/sections/water-prevention/containers/gis.js index e5d0985..037d266 100644 --- a/super-screen/client/src/sections/water-prevention/containers/gis.js +++ b/super-screen/client/src/sections/water-prevention/containers/gis.js @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import { connect } from 'react-redux'; import { render } from 'react-dom'; import { data as heatmapData } from './data' +import { SHUI_ZHAN } from '../constants/water'; import './gis.less' const MAPDOMID = 'fs-amap-container1'; let map = null; @@ -15,8 +16,10 @@ const MARKER_IMG_NAME = { markeryellow: '廉租房', } function Map(props) { + const { trendData, waterLevelAlarms } = props; const [delay, setDelay] = useState(true) const [tab, setTab] = useState('overview') + // 地图初始化 const loadMap = () => { // 图片图层 实现瓦片地图中国地图样式 bounds 第一个点为左下角 第二个点为右上角 @@ -74,8 +77,6 @@ function Map(props) { renderLayer() createText() }, 1000); - - }; const createText = () => { @@ -145,7 +146,7 @@ function Map(props) { // 初始化GIS 组件销毁清空定时器 useEffect(() => { loadMap(); - }, []); + }, [trendData, waterLevelAlarms]); const renderMarkers = () => { map.clearMap(); @@ -156,18 +157,18 @@ function Map(props) { if (loca && heatmap) loca.remove(heatmap) //初始层级 zoom14以下显示聚合点 - const data = [ - { lng: 116.117906, lat: 28.678096, type: 'home', name: '泵站1', kind: 'markergreen' }, - { lng: 116.195238, lat: 28.842114, type: 'home', name: '泵站2', kind: 'markerblue' }, - { lng: 116.037227, lat: 28.558811, type: 'home', name: '泵站3', kind: 'markeryellow' }, - { lng: 115.925856, lat: 28.558811, type: 'home', name: '泵站4', kind: 'markergreen' }, - { lng: 115.989847, lat: 28.484411, type: 'home', name: '泵站5', kind: 'markergreen' }, - ] + // const data = [ + // { lng: 116.117906, lat: 28.678096, type: 'home', name: '泵站1', kind: 'markergreen' }, + // { lng: 116.195238, lat: 28.842114, type: 'home', name: '泵站2', kind: 'markerblue' }, + // { lng: 116.037227, lat: 28.558811, type: 'home', name: '泵站3', kind: 'markeryellow' }, + // { lng: 115.925856, lat: 28.558811, type: 'home', name: '泵站4', kind: 'markergreen' }, + // { lng: 115.989847, lat: 28.484411, type: 'home', name: '泵站5', kind: 'markergreen' }, + // ] //初始点位显示 - data.map((x, index) => { + SHUI_ZHAN.map((x, index) => { var marker = new AMap.Marker({ - position: new AMap.LngLat(x.lng, x.lat), + position: new AMap.LngLat(x.location[0], x.location[1]), // 将一张图片的地址设置为 icon icon: '/assets/images/homepage/water/_monitor.png', // 设置了 icon 以后,设置 icon 的偏移量,以 icon 的 [center bottom] 为原点 @@ -250,14 +251,25 @@ function Map(props) { } const renderAlarms = () => { - if (tab == 'person') { + if (tab == 'person' || !waterLevelAlarms.length || !trendData.length) { return; } - - const alarms = [ - { lng: 116.044599, lat: 28.663869, type: 'device', name: 'xxx水位1', kind: 'markeralarm' }, - ] - + const alarms = waterLevelAlarms.map(a => { + let alarm = { + lng: a.lng, + lat: a.lat, + // type: 'device', + name: a.alarms[0]?.source.name, + kind: 'markeralarm' + }; + for (const t of trendData) { + if (t?.id == a.alarms[0]?.source.id) { + alarm.waterLevel = t.waterLevel; + alarm.alert = t.alert; + } + } + return alarm; + }) alarms.map((x, index) => { var marker = new AMap.Marker({ position: new AMap.LngLat(x.lng, x.lat), @@ -266,13 +278,14 @@ function Map(props) { // 设置了 icon 以后,设置 icon 的偏移量,以 icon 的 [center bottom] 为原点 offset: new AMap.Pixel(-13, -30), zooms: [3, 14], + zIndex: 13, }); marker.setTitle(x.name); map.add(marker); let infowindow = new AMap.InfoWindow({ isCustom: true, //使用自定义窗体 - content: `
+ content: `
`, offset: new AMap.Pixel(233, 260) }); @@ -301,11 +314,11 @@ function Map(props) {
实时水位 - 7.86m + {x.waterLevel}m
预警水位 - 7.86m + {x.alert}m
近1h雨量 @@ -405,12 +418,13 @@ function Map(props) { const renderLayer = () => { if (!loca) return; - const dataSource = [ - { lng: 116.194039, lat: 28.712472, data: 14 }, - { lng: 116.051963, lat: 28.546815, data: 25 }, - { lng: 116.064207, lat: 28.694385, data: 35 }, - { lng: 115.983192, lat: 28.345722, data: 20 } - ] + // const dataSource = [ + // { lng: 116.194039, lat: 28.712472, data: 14 }, + // { lng: 116.051963, lat: 28.546815, data: 25 }, + // { lng: 116.064207, lat: 28.694385, data: 35 }, + // { lng: 115.983192, lat: 28.345722, data: 20 } + // ] + const dataSource = trendData; if (gridLayer && loca) loca.remove(gridLayer) @@ -462,25 +476,25 @@ function Map(props) { // } height: function (index, feature) { const baseHeigh = 1200 - return feature.coordinates[0].properties.x.data > 30 ? baseHeigh * 6 : - feature.coordinates[0].properties.x.data > 25 ? baseHeigh * 5 : - feature.coordinates[0].properties.x.data > 20 ? baseHeigh * 4 : - feature.coordinates[0].properties.x.data > 15 ? baseHeigh * 3 : - feature.coordinates[0].properties.x.data > 10 ? baseHeigh * 2 : - feature.coordinates[0].properties.x.data > 0 ? baseHeigh * 1 : 0 + return feature.coordinates[0].properties.x.waterLevel > 30 ? baseHeigh * 6 : + feature.coordinates[0].properties.x.waterLevel > 25 ? baseHeigh * 5 : + feature.coordinates[0].properties.x.waterLevel > 20 ? baseHeigh * 4 : + feature.coordinates[0].properties.x.waterLevel > 15 ? baseHeigh * 3 : + feature.coordinates[0].properties.x.waterLevel > 10 ? baseHeigh * 2 : + feature.coordinates[0].properties.x.waterLevel > 0 ? baseHeigh * 1 : 0 }, topColor: function (index, feature) { - return feature.coordinates[0].properties.x.data > 30 ? 'rgba(255,136,140,1)' : 'rgba(117,154,206,1)' + return feature.coordinates[0].properties.x.waterLevel > 30 ? 'rgba(255,136,140,1)' : 'rgba(117,154,206,1)' }, sideColor: function (index, feature) { - return feature.coordinates[0].properties.x.data > 30 ? 'rgba(255,136,140,1)' : 'rgba(117,154,206,1)' + return feature.coordinates[0].properties.x.waterLevel > 30 ? 'rgba(255,136,140,1)' : 'rgba(117,154,206,1)' }, sideTopColor: function (index, feature) { - return feature.coordinates[0].properties.x.data > 30 ? 'rgba(255,136,140,1)' : 'rgba(117,154,206,1)' + return feature.coordinates[0].properties.x.waterLevel > 30 ? 'rgba(255,136,140,1)' : 'rgba(117,154,206,1)' }, sideBottomColor: function (index, feature) { - return feature.coordinates[0].properties.x.data > 30 ? 'rgba(255,136,140,0.5)' : 'rgba(117,154,206,0.5)' + return feature.coordinates[0].properties.x.waterLevel > 30 ? 'rgba(255,136,140,0.5)' : 'rgba(117,154,206,0.5)' }, }); gridLayer = gridLayer @@ -495,7 +509,7 @@ function Map(props) { cursor: 'pointer', angle: 0, // visible: false, - offset: [0, -s.data], + offset: [0, -s.waterLevel], style: { 'padding': '5px 10px', 'margin-bottom': '1rem', @@ -511,7 +525,7 @@ function Map(props) { }); text.setMap(map); text.show(); - text.setText(s.data + 'm') + text.setText(`${s.waterLevel || '--'}m`) text.setPosition(new AMap.LngLat(s.lng, s.lat)); }) @@ -523,7 +537,7 @@ function Map(props) { // if (feat) { // text.show(); - // text.setText(feat.coordinates[0].properties.x.data + 'm'); + // text.setText(feat.coordinates[0].properties.x.waterLevel + 'm'); // text.setPosition(e.lnglat); // } else { diff --git a/super-screen/client/src/sections/water-prevention/containers/gis.less b/super-screen/client/src/sections/water-prevention/containers/gis.less index cf4e6af..84cedc0 100644 --- a/super-screen/client/src/sections/water-prevention/containers/gis.less +++ b/super-screen/client/src/sections/water-prevention/containers/gis.less @@ -199,15 +199,17 @@ .gis-infowindow-alarm { background: url('/assets/images/homepage/communtity/alarminfowindow.png') no-repeat; - background-size: 100% 100% !important; - height: 380px !important; + background-size: 100% 100%; + height: 380px; width: 311px; - padding-top: 137px !important; + padding-top: 137px; + padding-left: 22px; + color: #fff; .gis_exit { cursor: pointer; position: absolute; - right: 2px; + right: 8px; top: 15px; width: 30.75px; height: 23px; @@ -219,8 +221,32 @@ margin-bottom: 10px; height: 35px; background-image: linear-gradient(180deg, #a7050591 0%, #48010191 100%); - ; + width: 93%; + display: flex; + align-items: center; + padding-left: 10px; + + .gis_title { + font-family: SourceHanSansCN-Regular; + font-weight: 400; + font-size: 14px; + color: #C3E6FF; + letter-spacing: 0; + margin-right: 12px; + } + .gis_text { + font-family: SourceHanSansCN-Regular; + font-weight: 400; + font-size: 14px; + color: #FFFFFF; + letter-spacing: 0; + line-height: 21px; + max-width: 183px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } } .confirm_text { @@ -262,8 +288,8 @@ line-height: 30px; width: 81px; height: 30px; - background-image: linear-gradient(180deg, #C50000 1%, #b600006e 52%, #C50000 100%); - box-shadow: inset 0 1px 14px 0 #ff2b2b59; + background-image: linear-gradient(180deg, #C50000 1%, #b600006e 52%, #C50000 100%) !important; + box-shadow: inset 0 1px 14px 0 #ff2b2b59 !important; border-radius: 4px; font-family: SourceHanSansCN-Medium; font-weight: 500; diff --git a/super-screen/client/src/sections/water-prevention/containers/homePage.js b/super-screen/client/src/sections/water-prevention/containers/homePage.js index 4bee23a..0c744a9 100644 --- a/super-screen/client/src/sections/water-prevention/containers/homePage.js +++ b/super-screen/client/src/sections/water-prevention/containers/homePage.js @@ -16,18 +16,155 @@ import moment from 'moment'; import './style.less' function homePage(props) { - const { dispatch } = props; + const { dispatch, actions } = props; + const { + getPcode, waterLogin, getWaterLevelTrend, getWaterStructures, getWaterVideoList, getYsAccessToken, getWaterAlarms, + worksafetyLogin, getEmerOrgList, getEmerTeamList, getEmerExpertList, getEmerMedicalList, getEmerRefugeList + } = actions.waterprevention; const childStyle = { height: '49%', color: '#fff', marginBottom: 17 } const cardHeight = document.body.clientHeight * 0.896 * 0.32 const cardContentHeight = cardHeight - 42 - 13 const [tab, setTab] = useState('overview');// overview emergency const [emengencyTab, setEmengencyTab] = useState('yjjg'); + const [trendData, setTrendData] = useState([]); + const [videoList, setVideoList] = useState([]); + const [accessToken, setAccessToken] = useState(''); + const [structures, setStructures] = useState([]); + const [waterLevelAlarms, setWaterLevelAlarms] = useState([]); + const [emerResource, setEmerResource] = useState([]); const SCREEN_COMPONENTS = { overview: { leftTop: LeftTop, leftBottom: LeftBottom, rightTop: RightTop, rightBottom: RightBottom }, emergency: { leftTop: EmergencyLeftTop, leftBottom: EmergencyLeftBottom, rightTop: EmergencyRightTop, rightBottom: EmergencyRightBottom } } const currentContainer = SCREEN_COMPONENTS[tab]; + + useEffect(() => { + getData(); + }, []) + + const getData = () => { + // 水务 + dispatch(getPcode()).then(res => { + if (res.success) { + dispatch(waterLogin({ username: "123456", password: "123456", pcode: res.payload.data.pcode })).then(loginRes => { + if (loginRes.success) { + const { token } = loginRes.payload.data; + sessionStorage.setItem('waterUser', JSON.stringify(loginRes.payload.data)); + // 水位趋势 + dispatch(getWaterLevelTrend({ orgId: 84, keyword: '', token })).then(trendRes => { + let fifterData = [] + if (trendRes.success) { + trendRes.payload.data.map((item, index) => { + let alert = item.alert ? item.alert.replace('(', '').replace(')', '').split(',')[0] : '--' + let historyMax = item.historyMax ? item.historyMax.replace('(', '').replace(')', '').split(',')[0] : '--' + let waterLevel = item.data.length != 0 ? item.data[item.data.length - 1].waterLevel : '' + let lastWaterLevel = item.data.length != 0 ? item.data[item.data.length - 2].waterLevel : '' + let currentTime = item.data.length != 0 ? moment(item.data[item.data.length - 1].time).format('YYYY-MM-DD HH:mm:ss') : '' + let currentState = item.data.length != 0 ? (item.alert && alert <= waterLevel ? '超警戒' : '正常') : '' + let futureState = '' + if (item.alert && item.futureData && item.futureData.length != 0) { + item.futureData.map((futureItem) => { + if (futureItem.value >= alert) { + futureState = `超警戒,预计${item.forecast_time}时水位抵达${(item.value).toFixed(2)}m` + } + }) + futureState = futureState != '' ? futureState : '正常' + } + fifterData.push({ + index: index + 1, + name: item.name, + currentState, + waterLevel, + lastWaterLevel, + currentTime, + alert, + historyMax, + futureState, + lat: item.latitude, + lng: item.longitude, + id: item.id + }) + }) + } + setTrendData(fifterData) + }) + // 结构物 + dispatch(getWaterStructures({ token })).then(structRes => { + if (structRes.success) { + setStructures(structRes.payload.data); + // 视频 + let promiseArr = [ + dispatch(getWaterVideoList(structRes.payload.data?.map(s => s.id).join(), { token })), + dispatch(getYsAccessToken({ token })) + ]; + Promise.all(promiseArr).then(resArr => { + if (resArr[0].success && resArr[1].success) { + const at = resArr[1].payload.data.accessToken; + const list = resArr[0].payload.data?.map(v => ({ url: v.yingshi.hlsAddress, name: v.name, structId: v.structId })); + setAccessToken(at) + setVideoList(list); + } + }) + // 告警 + dispatch(getWaterAlarms({ token })).then(alarmRes => { + if (alarmRes.success) { + let levelAlarms = []; + alarmRes.payload.data.alarms.forEach(a => { + let alarm = { ...a } + for (const struct of structRes.payload.data) { + if (struct.id === a.structureId && struct.type.name === '河流') { + alarm.lng = struct.longitude; + alarm.lat = struct.latitude; + levelAlarms.push(alarm); + } + }; + }) + setWaterLevelAlarms(levelAlarms); + } + }) + } + }) + } + }) + } + }) + // 安监 + dispatch(worksafetyLogin({ p: 'anjianshidian', username: 'SuperAdmin', password: '123456' })).then(res => { + if (res.success) { + sessionStorage.setItem('worksafetyUser', JSON.stringify(res.payload.data)); + getEmerResource(emengencyTab, res.payload.data.token) + } + }) + } + + const getEmerResource = (type, token) => { + if (type && token) { + const handleRequestComplete = (res) => { + if (res.success) setEmerResource(res.payload.data); + } + switch (type) { + case 'yjjg': + dispatch(getEmerOrgList({ token })).then(handleRequestComplete); + break; + case 'yjdw': + dispatch(getEmerTeamList({ token })).then(handleRequestComplete); + break; + case 'yjzj': + dispatch(getEmerExpertList({ token })).then(handleRequestComplete); + break; + case 'yljg': + dispatch(getEmerMedicalList({ token })).then(handleRequestComplete); + break; + case 'bncs': + dispatch(getEmerRefugeList({ token })).then(handleRequestComplete); + break; + default: + break; + } + } + } + return <>
@@ -43,7 +180,7 @@ function homePage(props) {
- +
@@ -54,16 +191,20 @@ function homePage(props) { }} changeEmengencyTab={(e) => { setEmengencyTab(e) + const { token } = JSON.parse(sessionStorage.getItem('worksafetyUser')); + getEmerResource(e, token) }} + trendData={trendData} + waterLevelAlarms={waterLevelAlarms} />
- +
- +
@@ -78,7 +219,6 @@ function mapStateToProps(state) { return { clientHeight: global.clientHeight, actions: global.actions, - }; } diff --git a/super-screen/client/src/utils/webapi.js b/super-screen/client/src/utils/webapi.js index 0245b23..8373f08 100644 --- a/super-screen/client/src/utils/webapi.js +++ b/super-screen/client/src/utils/webapi.js @@ -116,6 +116,21 @@ export const ApiTable = { delServiceManagement: 'service-management/{id}', lookField: 'lookField', + //大屏-水务 + getPcode: '_water/get/pcode', + waterLogin: '_water/_api/login', + getWaterLevelTrend: '_water/_api/overview/monitoring-station', + getWaterStructures: '_water/_api/organizations/84/structures', + getWaterVideoList: '_water/_api/structures/{struIds}/ipcs?stationIds=undefined', + getYsAccessToken: '_water/_api/yingshi/accessToken', + getWaterAlarms: '_water/_api/axy/alarm?userId=1134&orderBy=endTime&orderDirection=desc&limit=10&offset=0', + //安监 + worksafetyLogin: '_worksafety/_api/project/login', + getEmerOrgList: '_worksafety/_api/emergency/resource-org-list', // 应急机构 + getEmerTeamList: '_worksafety/_api/emergency/resource-team-list', // 应急队伍 + getEmerExpertList: '_worksafety/_api/emergency/resource-expert-list', // 应急专家 + getEmerMedicalList: '_worksafety/_api/emergency/resource-medical-list', // 医疗机构 + getEmerRefugeList: '_worksafety/_api/emergency/resource-refuge-list', // 避难场所 }; export const RouteTable = { diff --git a/super-screen/config.js b/super-screen/config.js index 685e278..b953cc9 100644 --- a/super-screen/config.js +++ b/super-screen/config.js @@ -26,6 +26,7 @@ const ANXINCLOUD_QINIU_ACCESSKEY = process.env.ANXINCLOUD_QINIU_ACCESSKEY || fla const ANXINCLOUD_QINIU_SECRETKEY = process.env.ANXINCLOUD_QINIU_SECRETKEY || flags.qnsk; const ANXINCLOUD_QINIU_BUCKET_RESOURCE = process.env.ANXINCLOUD_QINIU_BUCKET_RESOURCE || flags.qnbkt; const ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE = process.env.ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE || flags.qndmn; +const weatherUrl = process.env.ANXINCLOUD_WEATHERAPP_API || "https://weatherssj.anxinyun.cn/weatherApp/weather/getImmeData" //阿里OSS const ALI_OSS_ACCESSKEY = process.env.ALI_OSS_ACCESSKEY || flags.aliOssAccessKey; @@ -84,7 +85,8 @@ const product = { secretKey: ALI_OSS_SECRETKET, bucket: ALI_OSS_BUCKET, region: ALI_OSS_REGION - } + }, + weatherUrl, } }, { diff --git a/super-screen/package.json b/super-screen/package.json index b2a7a71..a62f703 100644 --- a/super-screen/package.json +++ b/super-screen/package.json @@ -69,7 +69,7 @@ "@fs/attachment": "^1.0.0", "@jiaminghi/data-view-react": "^1.2.5", "@peace/components": "0.0.35", - "@peace/utils": "0.0.37", + "@peace/utils": "0.0.66", "ahooks": "^3.7.4", "ali-oss": "^6.17.1", "antd": "^4.24.5", diff --git a/super-screen/routes/weather/index.js b/super-screen/routes/weather/index.js new file mode 100644 index 0000000..cd175bb --- /dev/null +++ b/super-screen/routes/weather/index.js @@ -0,0 +1,55 @@ +const request = require('superagent'); + +let _weatherUrl = ''; +module.exports.entry = function (app, router, opts) { + const { weatherUrl } = opts; + console.log(weatherUrl, '天气'); + _weatherUrl = weatherUrl; + // _weatherUrl = "https://weatherssj.anxinyun.cn/weatherApp/weather/getImmeData" + + router.get('/query/weather', weather); +}; + +async function weather(ctx) { + try { + const { cname } = ctx.query; + const reg = /.+?(省|市|自治区|自治州|县|区)/g; + const arr = cname.match(reg); + if (Array.isArray(arr)) { + let cityName = arr[0]; + if (arr[1]) { + cityName = arr[1]; + } + const weatherRes = await request.post(_weatherUrl).send({ cityName }); + const { body } = weatherRes; + if (body && body.data) { + ctx.status = 200; + ctx.body = { ...body.data, cname, texta: _weatherUrl }; + } else { + ctx.status = 400; + ctx.body = { msg: '获取天气错误' }; + } + } else { + ctx.status = 400; + ctx.body = { msg: '地址解析错误' }; + } + } catch (error) { + console.log('[*err]', error); + ctx.status = 400; + ctx.body = error; + } +} + +// ip地址识别库 +// 淘宝IP地址查询接口:http://ip.taobao.com/service/getIpInfo.php?ip=1.1.35.57 +// 腾讯IP地址查询接口:http://fw.qq.com/ipaddress +// 新浪的IP地址查询接口:http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js +// 新浪多地域测试方法:http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js&ip=218.192.3.42 +// 搜狐IP地址查询接口(默认GBK):http://pv.sohu.com/cityjson +// 搜狐IP地址查询接口(可设置编码):http://pv.sohu.com/cityjson?ie=utf-8 +// 搜狐另外的IP地址查询接口:http://txt.go.sohu.com/ip/soip +// 谷歌IP地址查询接口:http://j.maxmind.com/app/geoip.js +// 1616 IP地址查询接口:http://w.1616.net/chaxun/iptolocal.php +// 126 http://ip.ws.126.net/ipquery hao123 +// http://app.hao123.com/ipquery/getcity.php?rtype=2 +// 太平洋电脑网 http://whois.pconline.com.cn/ \ No newline at end of file