1. 调整moment本地化问题

2. 功能完善与调整
This commit is contained in:
王铜 2021-01-04 17:15:44 +08:00
parent 07c264a6f8
commit 2e8f41d5d0
28 changed files with 2398 additions and 148 deletions

View File

@ -16,18 +16,22 @@
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong> Please enable it to continue.</strong>
</noscript> </noscript>
<div id="app"></div> <div id="app"></div>
<!-- built files will be auto injected -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-router@3.1.3"></script> <script src="https://cdn.jsdelivr.net/npm/vue-router@3.1.3"></script>
<script src="https://cdn.jsdelivr.net/npm/vuex@3.6.0"></script> <script src="https://cdn.jsdelivr.net/npm/vuex@3.6.0"></script>
<script src="https://cdn.jsdelivr.net/npm/js-md5@0.7.3"></script> <script src="https://cdn.jsdelivr.net/npm/js-md5@0.7.3"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1"></script> <script src="https://cdn.jsdelivr.net/npm/moment@2.29.1"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1/dist/locale/zh-cn.js"></script>
<script src="https://cdn.jsdelivr.net/npm/ant-design-vue@1.7.2/dist/antd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/ant-design-vue@1.7.2/dist/antd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.21.0/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios@0.21.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@4.9.0/dist/echarts.common.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/echarts@4.9.0/dist/echarts.common.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/v-charts@1.19.0/lib/index.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/v-charts@1.19.0/lib/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script> <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
<!-- built files will be auto injected -->
</body> </body>
</html> </html>

View File

@ -16,14 +16,14 @@
> >
<img :src="logo" /> <img :src="logo" />
<p> <p>
<span style="color:white;font-size: 26px;font-weight: bold;text-shadow: 0 0 10px black;">CVEO水质监测平台</span> <span style="color:white;font-size: 26px;font-weight: bold;text-shadow: 0 0 10px black;">湖北富瑞尔科技有限公司水质监测平台</span>
</p> </p>
<div style="color:white;font-size: 13px;font-weight: bold;text-shadow: 0 0 7px black;"> <div style="color:white;font-size: 13px;font-weight: bold;text-shadow: 0 0 7px black;">
<p> <p>
当前版本: 1.0.2 当前版本: 1.1.0
</p> </p>
<p> <p>
后台版本: 1.0.0 后台版本: 1.1.0
</p> </p>
<p> <p>
版本记录 版本记录
@ -39,6 +39,11 @@
<p> <p>
2020-12-7 2020-12-7
1.0.2版本修复字体背景遮挡问题 1.0.2版本修复字体背景遮挡问题
</p>
<p>
2021-1-4
1.1.0版本添加分时数据查询功能提供表格与图表两种方式对数据时间范围进行限制
后台进行调整对分时数据时间范围进行限制
</p> </p>
</div> </div>
</a-col> </a-col>
@ -149,12 +154,15 @@ export default {
*/ */
let results = [ let results = [
getData(URL_MAP.DEVICE_LIST), getData(URL_MAP.DEVICE_LIST),
getData(URL_MAP.SENSOR_LIST) getData(URL_MAP.SENSOR_LIST),
getData(URL_MAP.PROJECT_LIST),
getData(URL_MAP.RULES_LIST),
] ]
data = await Promise.all(results) data = await Promise.all(results)
this.$store.commit('deviceList', data[0].data) this.$store.commit('deviceList', data[0].data)
this.$store.commit('sensorList', data[1].data) this.$store.commit('sensorList', data[1].data)
this.$store.commit('projectList', data[2].data)
this.$store.commit('ruleList', data[3].data)
let path = decodeURIComponent(this.$route.query.redirect || "/"); let path = decodeURIComponent(this.$route.query.redirect || "/");
this.$router.push({ path: path }); this.$router.push({ path: path });
} catch (e) { } catch (e) {

View File

@ -2,10 +2,20 @@
<a-layout> <a-layout>
<a-layout-header style="height: 110px"> <a-layout-header style="height: 110px">
<a-row> <a-row>
<a-col :span="12" style="color: white"> <a-col :span="11" style="color: white">
<span class="w-title">水质监测平台</span> <span class="w-title">水质监测平台</span>
</a-col> </a-col>
<a-col :span="2" :offset="10"> <div style="position:absolute;right:100px;">
<span style="margin-right: 24px">
<a-avatar @click="dayLimit=true" style="background-color:#f56a00" shape="square" icon="exclamation-circle"/>
</span>
<span >
<a-badge :count="1"
><a-avatar @click="dayLogs=true" style="background-color:#ff0000" shape="square" icon="warning" />
</a-badge>
</span>
</div>
<div style="position:absolute;right:0;">
<a-popover :title="username" trigger="hover" placement="bottom"> <a-popover :title="username" trigger="hover" placement="bottom">
<template slot="content"> <template slot="content">
<p v-if="!isAnyone" style="cursor: pointer" @click="changePasswd"> <p v-if="!isAnyone" style="cursor: pointer" @click="changePasswd">
@ -15,7 +25,7 @@
</template> </template>
<a-avatar :size="48" icon="user" /> <a-avatar :size="48" icon="user" />
</a-popover> </a-popover>
</a-col> </div>
</a-row> </a-row>
<a-row> <a-row>
<a-menu <a-menu
@ -32,7 +42,25 @@
><icon-font type="wt-iconsearch" />数据查询</span ><icon-font type="wt-iconsearch" />数据查询</span
> >
<a-menu-item key="detail-search"> <a-menu-item key="detail-search">
<icon-font type="wt-iconxiangxi" />详细数据 <icon-font type="wt-iconxiangxi" />分时数据
</a-menu-item>
<a-menu-item key="means">
<icon-font type="wt-iconxiangxi" />均值数据
</a-menu-item>
<a-menu-item key="qa-day">
<icon-font type="wt-iconxiangxi" />水质质量日报
</a-menu-item>
<a-menu-item key="lur-mean">
<icon-font type="wt-iconxiangxi" />水质指标统计
</a-menu-item>
<a-menu-item key="qa-sts">
<icon-font type="wt-iconxiangxi" />水质质量类别统计
</a-menu-item>
<a-menu-item key="lur-sts">
<icon-font type="wt-iconxiangxi" />水质指标超标统计
</a-menu-item>
<a-menu-item key="yl-sts">
<icon-font type="wt-iconxiangxi" />水质超标处理记录
</a-menu-item> </a-menu-item>
</a-sub-menu> </a-sub-menu>
<a-menu-item key="analysis"> <a-menu-item key="analysis">
@ -46,7 +74,7 @@
</a-layout-header> </a-layout-header>
<a-layout-content> <a-layout-content>
<keep-alive> <keep-alive>
<router-view /> <router-view />
</keep-alive> </keep-alive>
</a-layout-content> </a-layout-content>
<a-modal <a-modal
@ -57,35 +85,57 @@
> >
<password-change @ok="logout" @cancle="passwd_visible = false" /> <password-change @ok="logout" @cancle="passwd_visible = false" />
</a-modal> </a-modal>
<a-modal
v-model="dayLimit"
:title="'日均值限定'"
:destroyOnClose="true"
:footer="null"
>
<day-limit ></day-limit>
</a-modal>
<a-modal
v-model="dayLogs"
:title="'超标报警'"
:destroyOnClose="true"
:footer="null"
:width="900"
>
<day-logs ></day-logs>
</a-modal>
</a-layout> </a-layout>
</template> </template>
<script> <script>
import IconFont from "@/components/icon"; import IconFont from "@/components/icon";
import Logs from "@/components/logs/Index";
import { logout } from "@/utils/http"; import { logout } from "@/utils/http";
import PasswordChange from "./admin/forms/PasswordChange.vue"; import PasswordChange from "./admin/forms/PasswordChange.vue";
import DayLimit from './logs/DayLimit.vue';
import DayLogs from './logs/DayLogs.vue';
export default { export default {
components: { components: {
"icon-font": IconFont, "icon-font": IconFont,
PasswordChange, PasswordChange,
DayLimit,
DayLogs
}, },
data() { data() {
return { return {
current: ["map-layout"], current: ["map-layout"],
currentComp: "map-layout", currentComp: "map-layout",
passwd_visible: false, passwd_visible: false,
dayLimit:false,
dayLogs:false,
}; };
}, },
watch:{ watch: {
currentComp: function(old, newVal) { currentComp: function (old, newVal) {
if(old === newVal){ if (old === newVal) {
return return;
} }
this.$router.push({ this.$router.push({
name: this.currentComp name: this.currentComp,
}) });
} },
}, },
computed: { computed: {
isAdmin() { isAdmin() {
@ -115,10 +165,11 @@ export default {
}, },
}, },
mounted() { mounted() {
this.$router.push({ this.$router.push({
name:this.currentComp name: this.currentComp,
}) });
} },
}; };
</script> </script>

View File

@ -24,6 +24,11 @@
<a-icon type="user" /> <a-icon type="user" />
<span>人员</span> <span>人员</span>
</a-menu-item> </a-menu-item>
<a-menu-item key="rules-table">
<a-icon type="info" />
<span>规则</span>
</a-menu-item>
</a-menu> </a-menu>
</a-layout-sider> </a-layout-sider>
<a-layout> <a-layout>
@ -41,12 +46,14 @@ import DeviceTable from './tables/DeviceTable';
import ProjectTable from './tables/ProjectTable'; import ProjectTable from './tables/ProjectTable';
import UserTable from './tables/UserTable'; import UserTable from './tables/UserTable';
import { getData, URL_MAP } from "@/utils/http"; import { getData, URL_MAP } from "@/utils/http";
import RulesTable from './tables/RulesTable';
export default { export default {
components: { components: {
'sensor-table':SensorTable, 'sensor-table':SensorTable,
'device-table':DeviceTable, 'device-table':DeviceTable,
'project-table':ProjectTable, 'project-table':ProjectTable,
'user-table':UserTable 'user-table':UserTable,
'rules-table':RulesTable
}, },
data() { data() {
return { return {

View File

@ -0,0 +1,165 @@
<template>
<div>
<a-form-model
:model="form"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 14 }"
>
<a-form-model-item label="项目">
<a-select
show-search
v-model="form.project"
@search="searchPorjects"
:filter-option="false"
:not-found-content="fetching ? undefined : null"
>
<a-spin v-if="fetching" slot="notFoundContent" size="small" />
<a-select-option v-for="u in projects" :key="u._id">
{{ u.name }}
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="设备">
<a-select
show-search
v-model="form.device"
@search="searchDevices"
:filter-option="false"
:not-found-content="fetching ? undefined : null"
>
<a-spin v-if="fetching" slot="notFoundContent" size="small" />
<a-select-option v-for="u in devices" :key="u._id">
{{ u.device_name }}
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="参数">
<a-select
show-search
v-model="form.sensor"
@search="searchSensors"
:filter-option="false"
:not-found-content="fetching ? undefined : null"
>
<a-spin v-if="fetching" slot="notFoundContent" size="small" />
<a-select-option v-for="u in sensors" :key="u._id">
{{ u.name }}
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="范围">
<span><a-input-number v-model="form.allow_min" ></a-input-number> - <a-input-number v-model="form.allow_max"></a-input-number></span>
</a-form-model-item>
<a-form-model-item :wrapper-col="{ span: 14, offset: 4 }">
<a-button type="primary" @click="onSubmit"> 确认 </a-button>
<a-button style="margin-left: 10px" @click="$emit('cancle')">
关闭
</a-button>
</a-form-model-item>
</a-form-model>
</div>
</template>
<script>
import { mergeList } from "@/utils/utils";
import { getData, URL_MAP } from "@/utils/http";
export default {
data() {
return {
form: {
_id: "",
project: "",
device: "",
sensor: "",
allow_min: 0,
allow_max: 0,
one_error_duration: 0,
total_error_duration: 0,
total_duration: 0,
one_null_duration: 0,
total_null_duration: 0,
total_null: 0,
},
projects: [],
sensors: [],
devices: [],
fetching: false,
};
},
computed: {},
props: {
detail: Object,
},
methods: {
onSubmit() {
// console.log(this.form)
this.$emit("ok", this.form);
},
async searchPorjects(value) {
this.fetching = true;
try {
let data = await getData(URL_MAP.PROJECT_LIST, {
name__icontains: name,
});
this.projects = mergeList(data.data, this.projects);
} catch (e) {
console.log(e);
} finally {
this.fetching = false;
}
},
async searchSensors(name) {
this.fetching = true;
try {
let data = await getData(URL_MAP.SENSOR_LIST, {
name__icontains: name,
});
this.sensors = mergeList(data.data, this.sensors);
} catch (e) {
console.log(e);
} finally {
this.fetching = false;
}
},
async searchDevices(value) {
this.fetching = true;
try {
let data = await getData(URL_MAP.DEVICE_LIST, {
device_name__icontains: name,
});
this.devices = mergeList(data.data, this.devices);
} catch (e) {
console.log(e);
} finally {
this.fetching = false;
}
},
},
mounted() {
for (let key in this.form) {
this.form[key] = this.detail[key];
}
if(this.form.project) {
this.projects = [ Object.assign({}, this.form.project) ]
this.form.project = this.form.project._id
}
if(this.form.device) {
this.devices = [ Object.assign({}, this.form.device) ]
this.form.device = this.form.device._id
}
if(this.form.sensor) {
this.sensors = [ Object.assign({}, this.form.sensor) ]
this.form.sensor = this.form.sensor._id
}
// this.form.project = this.form.managers.map(v => v._id)
// this.form.normals = this.form.normals.map(v => v._id)
// this.managers = this.detail.managers
// this.normals = this.detail.normals
},
};
</script>
<style>
</style>

View File

@ -139,7 +139,7 @@ export default {
try { try {
if (!this.isCreate) { if (!this.isCreate) {
form["id"] = form["_id"]; form["id"] = form["_id"];
console.log(form) // console.log(form)
let data = await patchJSON(URL_MAP.DEVICE_DETAIL, form); let data = await patchJSON(URL_MAP.DEVICE_DETAIL, form);
} else { } else {
form["id"] = form["_id"]; form["id"] = form["_id"];

View File

@ -0,0 +1,177 @@
<template>
<div style="margin: 20px">
<a-row style="margin-bottom: 20px">
<a-button type="primary" @click="handleCreate">添加</a-button>
</a-row>
<a-table
:columns="tableCols"
:data-source="tableRows"
:loading="loading"
:rowKey="idName"
@change="handleTableChange"
>
<span slot="device" slot-scope="device">
<a-tag >
{{ device ? device.device_name : 'null' }}
</a-tag>
</span>
<span slot="sensor" slot-scope="sensor">
<a-tag >
{{ sensor ? sensor.name : 'null' }}
</a-tag>
</span>
<span slot="normals" slot-scope="normals">
<a-tag v-for="item in normals" :key="item._id">
{{ item.name }}
</a-tag>
</span>
<span slot="action" slot-scope="record">
<a-button size="small" type="link" @click="() => handleDetail(record)"
>查看</a-button
>
<a-divider type="vertical" />
<a-button type="danger" size="small" @click="() => handleDelete(record)"
>删除</a-button
>
</span>
</a-table>
<a-modal
v-model="visible"
:title="isCreate ? '添加项目' : '查看项目'"
:destroyOnClose="true"
:footer="null"
>
<rule-detail
:detail="record"
@ok="handleOk"
@cancle="visible = false"
/>
</a-modal>
</div>
</template>
<script>
import { deleteJSON, getData, patchJSON, postJSON, URL_MAP } from "@/utils/http";
import RuleDetail from '../forms/RuleDetail.vue';
export default {
components: {
RuleDetail,
},
data() {
return {
tableRows: [],
tableCols: [
{
dataIndex: "device",
key: "device",
title: "设备名",
scopedSlots: { customRender: "device" },
},
{
dataIndex: "sensor",
key: "sensor",
title: "参数",
scopedSlots: { customRender: "sensor" },
},
{
dataIndex: "allow_min",
key: "allow_min",
title: "最小值",
},
{
dataIndex: "allow_max",
key: "allow_max",
title: "最大值",
},
{
key: "action",
title: "操作",
scopedSlots: { customRender: "action" },
},
],
pages: {},
loading: false,
listUrl: URL_MAP.RULES_LIST,
detailUrl: URL_MAP.RULES_DETAIL,
idName: "_id",
visible: false,
record: {},
isCreate: false,
};
},
methods: {
handleTableChange(pagination, filters, sorter) {
console.log(pagination);
},
async fetch(params) {
this.loading = true;
try {
let results = await getData(this.listUrl, params);
let data = results.data;
const pagination = { ...this.pages };
pagination.total = data.length;
this.tableRows = data;
this.pages = pagination;
} catch (e) {
console.log(e);
} finally {
this.loading = false;
}
},
handleDetail(record) {
this.visible = true;
this.record = record;
},
async handleDelete(record) {
this.loading = true
try{
await deleteJSON(this.detailUrl, { 'id': record._id })
await this.fetch({})
}catch(e)
{
console.log(e)
}
finally{
this.loading = false
}
},
async handleOk(form) {
this.visible = false;
this.loading = true;
try {
if (!this.isCreate) {
form["id"] = form[this.idName];
let data = await patchJSON(this.detailUrl, form);
} else {
form[this.idName] = undefined;
let data = await postJSON(this.listUrl, form);
}
await this.fetch({});
} catch (e) {
console.log(e);
} finally {
this.isCreate = false;
this.loading = false;
}
},
handleCreate() {
this.isCreate = true;
this.visible = true
this.record = {
device: null,
project: null,
sensor: null,
};
},
},
mounted() {
this.fetch({});
},
};
</script>
<style>
</style>

View File

@ -1,13 +1,129 @@
<template> <template>
<div></div> <div style="height: calc(100vh - 110px)">
<a-layout :theme="'light'" style="height: calc(100vh - 110px)">
<a-layout-sider :theme="'light'" :width="300" style="padding: 20px">
<div style="text-align: center; font-size: 16px">选择站点</div>
<a-divider />
<a-tree
v-model="checkedKeys"
checkable
:selected-keys="selectedKeys"
:auto-expand-parent="true"
:tree-data="treeData"
@select="onSelect"
showLine
>
<a-icon slot="switcherIcon" type="down" />
</a-tree>
</a-layout-sider>
<a-layout style="padding: 20px">
<a-layout-header style="background: #fff; padding: 15px 0 5px 15px">
<a-radio-group default-value="a" button-style="solid" style="display: block; line-height: 32px">
<a-radio-button value="a"> 小时值分析 </a-radio-button>
<a-radio-button value="b"> 日均值分析 </a-radio-button>
<a-radio-button value="c"> 水质指标统计 </a-radio-button>
<a-radio-button value="d"> 质量等级统计 </a-radio-button>
<a-radio-button value="e"> 质量对比分析 </a-radio-button>
</a-radio-group>
<span>
检测项
<a-select placeholder="选择检测项" mode="multiple" style="min-width: 150px">
<a-select-option value="0"> pH </a-select-option>
</a-select>
日期
<a-range-picker
separator="至"
:placeholder="['开始日期', '结束日期']"
:defaultValue="datetimeRange"
@change="dateRangeChange"
></a-range-picker>
<a-button style="margin-left: 15px" type="primary" icon="bar-chart"
>分析</a-button
>
</span>
</a-layout-header>
<a-layout-content style="padding: 20px 0 20px 0">
<a-row>
图表类型
<a-select placeholder="选择类型" style="min-width: 150px">
<a-select-option value="0"> 单站点多指标分析 </a-select-option>
<a-select-option value="1"> 多站点多指标分析 </a-select-option>
</a-select>
</a-row>
</a-layout-content>
</a-layout>
</a-layout>
</div>
</template> </template>
<script> <script>
export default { export default {
data() {
return {
checkedKeys: ["all"],
autoExpandParent: true,
selectedKeys: [],
treeData: [],
datetimeRange: [],
columns: [],
data: [],
};
},
computed: {
sensorList() {
return this.$store.getters.sensorList;
},
deviceList() {
return this.$store.getters.deviceList;
},
},
methods: {
async fetch() {},
onSelect() {},
dateRangeChange() {},
},
mounted() {
const columns = [
{
title: "站点名",
dataIndex: "name",
},
{
title: "时间",
dataIndex: "time",
},
];
this.sensorList.forEach((sensor) => {
columns.push({
title: sensor.param_name,
children: [
{
title: sensor.uint || "-",
dataIndex: sensor.param_key,
},
],
});
});
} this.columns = columns;
const deviceTree = [];
this.deviceList.forEach((device) => {
deviceTree.push({
title: device.device_name,
key: device._id,
});
});
this.treeData = [
{
title: "设备",
key: "all",
children: deviceTree,
},
];
},
};
</script> </script>
<style> <style>
</style> </style>

View File

@ -0,0 +1,139 @@
<template>
<div>
<div
v-if="hasData"
id="index-chart"
ref="indexChart"
:style="{ width:width, height:height }"
/>
<a-empty v-else />
</div>
</template>
<script>
export default {
data() {
return {
chartId:'index-chart-'
};
},
props: {
width: String,
height: String,
rawData: Array,
setting: Object,
},
watch: {
rawData(val, newVal) {
this.initChart();
},
},
computed: {
hasData() {
return this.rawData.length > 0;
},
},
methods: {
initChart() {
let e = this.$refs['indexChart']
if(!e)return
let myChart = echarts.init(e);
const { title, xAxis, series, seriesName, seriesType, yRange } = this.setting;
let option = {
title: {
text: title,
left:'center'
},
tooltip: {
trigger: "axis",
},
xAxis: {
data: this.rawData.map(function (item) {
return item[xAxis];
}),
},
yAxis: {
min: yRange[0] - (yRange[1] - yRange[0]) * 0.05,
max: yRange[1] + (yRange[1] - yRange[0]) * 0.05,
splitLine: {
show: false
}
},
dataZoom: [
{
// startValue: '2014-06-01'
},
{
type: "inside",
},
],
toolbox: {
left: "left",
feature: {
dataZoom: {
yAxisIndex: "none",
},
restore: {},
saveAsImage: {},
},
},
visualMap: {
show: true,
top: 40,
right: 50,
type:'piecewise',
text:'图例',
pieces: [
{
gte: yRange[0],
lte: yRange[1],
color: '#0000ff',
},
{
gt: yRange[1],
color: "#ff0000",
},
{
gt: yRange[1],
color: "#ff0000",
},
],
categories:['合格','不合格'],
outOfRange: {
color: "#0000ff",
},
},
series: {
name: seriesName,
type: seriesType,
data: this.rawData.map(function (item) {
return item[series];
}),
markLine: {
silent: true,
data: [
{
yAxis: yRange[0],
},
{
yAxis: yRange[1],
},
],
},
},
};
console.log(option)
myChart.setOption(option);
},
},
mounted() {
// console.log(this.rawData)
this.initChart();
},
};
</script>
<style>
</style>

View File

View File

@ -0,0 +1,115 @@
<template>
<div>
<a-table style="margin-bottom:5px;border:solid 1px;" :columns="dayMeanColumns" :data-source="dayMeanData" :pagination="false" bordered>
</a-table>
<a-table style="margin-bottom:5px;border:solid 1px;" :columns="QaColumns" :data-source="QaData" :pagination="false" bordered>
</a-table>
<a-table style="margin-bottom:5px;border:solid 1px;" :columns="dayCountColumns" :data-source="dayCountData" :pagination="false" bordered>
</a-table>
</div>
</template>
<script>
export default {
data() {
return {
dayMeanColumns: [
{
title: "检测项",
dataIndex: "param_name",
},
{
title: "日均值最小值",
dataIndex: "day_min",
},
{
title: "日均值最大值",
dataIndex: "day_max",
},
{
title: "月超标限定",
dataIndex: "month",
},
],
QaColumns:[
{
title: "检测项",
dataIndex: "param_name",
},
{
title: "截至今日合格率",
dataIndex: "day_min",
},
{
title: "月度合格率",
dataIndex: "day_max",
},
{
title: "年度合格率",
dataIndex: "month",
},
],
dayCountColumns:[
{
title: "检测项",
dataIndex: "param_name",
},
{
title: "截至今日超标天数",
dataIndex: "day_min",
},
{
title: "剩余可超标天数",
dataIndex: "day_max",
},
{
title: "年度可超标天数",
dataIndex: "month",
},
],
dayMeanData: [
{
param_name:'pH值',
day_min:6.5,
day_max:7.5,
month:2
},
{
param_name:'余氯',
day_min:'-',
day_max:1,
month:2
},
{
param_name:'浊度',
day_min:0,
day_max:1,
month:1
}
],
QaData:[
{
param_name:'水质质量合格率',
day_min:'75%',
day_max:'82%',
month:'80%'
},
],
dayCountData:[
{
param_name:'浊度',
day_min:'2',
day_max:'5',
month:'30'
},
]
};
},
};
</script>
<style>
</style>

View File

@ -0,0 +1,81 @@
<template>
<div>
<a-table
style="margin-bottom: 5px; border: solid 1px"
:columns="columns"
:data-source="data"
bordered
>
<span slot="action">
<a-button type="primary" size="small">处理</a-button>
<a-divider type="vertical" />
<a-button size="small">忽略</a-button>
</span>
</a-table>
</div>
</template>
<script>
import Moment from '@/components/utils/moment.zh_cn'
export default {
data() {
return {
columns: [
{
title: "地点",
dataIndex: "location",
},
{
title: "详情",
dataIndex: "detail",
},
{
title: "数值",
dataIndex: "number",
},
{
title: "时间",
dataIndex: "time",
},
{
title: "处理情况",
dataIndex: "logs",
},
{
title: "操作",
key: "action",
scopedSlots: { customRender: "action" },
},
],
data: [
{
location: '武昌区',
detail: '翡翠一品游泳池 余氯超标',
number: 2.5,
time: Moment().date(12).format('lll'),
logs:'未处理',
},
{
location: '武昌区',
detail: '翡翠一品游泳池 余氯超标',
number: 1.5,
time: Moment().date(11).format('lll'),
logs:'未处理',
},
{
location: '武昌区',
detail: '翡翠一品游泳池 浊度超标',
number: 1.2,
time: Moment().date(3).format('lll'),
logs:'未处理',
},
],
};
},
};
</script>
<style>
</style>

View File

@ -1,13 +0,0 @@
<template>
<div></div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@ -8,6 +8,56 @@
type="primary" type="primary"
@click="loadBaseData" @click="loadBaseData"
></a-button> ></a-button>
<div class="map-tip">
<a-row style="margin-bottom: 10px; color: white"> 图例 </a-row>
<a-row :gutter="2">
<a-col :span="8">
<div>
<img :src="waterImg" width="25px" />
</div>
</a-col>
<a-col :span="16"> 水质检测仪 </a-col>
</a-row>
</div>
<div class="tags-bottom">
<a-row :gutter="2">
<a-col :span="6">
<div>
<a-tag color="#279DF2"> 正常 </a-tag>
</div>
</a-col>
<a-col :span="6">
<div>
<a-tag color="#F56A00"> 预警 </a-tag>
</div>
</a-col>
<a-col :span="6">
<div>
<a-tag color="#f00"> 超标 </a-tag>
</div>
</a-col>
<a-col :span="6">
<div>
<a-tag color="#888"> 离线 </a-tag>
</div>
</a-col>
</a-row>
</div>
<div class="right-botton" @click="rightShow = true" v-show="!rightShow">
<a-icon type="caret-left" />
</div>
<div class="right-table-container" v-if="rightShow">
<right-table @close="rightShow = false" />
</div>
<div class="top-sensor-bar">
<a-button-group>
<a-button type="primary" size="large"> 全选 </a-button>
<a-button size="large"> pH </a-button>
<a-button size="large"> 浊度 </a-button>
<a-button size="large"> 余氯 </a-button>
<a-button size="large"> 温度 </a-button>
</a-button-group>
</div>
</div> </div>
</template> </template>
@ -15,10 +65,12 @@
// import { fromLonLat, toLonLat } from "ol/proj.js"; // import { fromLonLat, toLonLat } from "ol/proj.js";
const { fromLonLat, toLonLat } = ol.proj; const { fromLonLat, toLonLat } = ol.proj;
import { getData, login, logout, URL_MAP } from "@/utils/http"; import { getData, login, logout, URL_MAP } from "@/utils/http";
import RightTable from "./RightTable.vue";
let map = null; let map = null;
export default { export default {
components: { RightTable },
data() { data() {
return { return {
view: new ol.View({ view: new ol.View({
@ -28,6 +80,8 @@ export default {
waterLayer: "", waterLayer: "",
projection: "", projection: "",
center: fromLonLat([114.3, 30.6]), center: fromLonLat([114.3, 30.6]),
waterImg: require("@/assets/water.png"),
rightShow: false,
}; };
}, },
computed: { computed: {
@ -125,4 +179,63 @@ export default {
</script> </script>
<style> <style>
.tags-bottom {
position: absolute;
bottom: 10px;
left: 200px;
width: 200px;
padding: 5px;
background-color: #00000052;
border-radius: 4px;
}
.map-tip {
position: absolute;
bottom: 10px;
left: 10px;
width: 180px;
background: #00000096;
padding: 10px 20px 20px 20px;
border-radius: 5px;
color: white;
}
.right-botton {
position: absolute;
right: 0px;
width: 20px;
height: 100px;
top: 50%;
border-radius: 5px 0 0 5px;
background-color: #096dd9b5;
padding: 40px 0 0 0;
color: white;
cursor: pointer;
}
.right-table-container {
position: absolute;
right: 0px;
width: 500px;
height: 700px;
bottom: 70px;
background: white;
border: solid 2px black;
opacity: 0.9;
padding: 20px;
border-radius: 10px 0 0 10px;
box-shadow: 1px 1px 14px 4px;
}
.top-sensor-bar {
position: absolute;
top: 130px;
width: fit-content;
background: #00000096;
margin: 0px auto;
left: 0;
right: 0;
/* padding: 10px; */
color: white;
border-radius: 5px;
align-items: center;
text-align: center;
box-shadow: 0px 0px 14px 4px #000000ad;
}
</style> </style>

View File

@ -0,0 +1,59 @@
<template>
<div>
<div @click="$emit('close')" style="position:absolute;right:20px;cursor:pointer;z-index:999;">
<a-icon type="close" />
</div>
<a-form :label-col="{ span: 6 }" :wrapper-col="{ span: 12 }">
<a-form-item label="站点类型">
<a-select placeholder="选择类型" >
<a-select-option value="0"> 水质监测仪 </a-select-option>
</a-select>
</a-form-item>
<a-form-item label="监测指标">
<a-select placeholder="选择指标">
<a-select-option :key="sensor._id" v-value="sensor.param_key" v-for="sensor in sensorList"> {{sensor.param_name}} </a-select-option>
</a-select>
</a-form-item>
</a-form>
<a-table style="border-top:solid" :columns="columns" :data-source="data"> </a-table>
</div>
</template>
<script>
export default {
data() {
return {
columns: [
{
title: "站点",
dataIndex: "name",
key: "name",
},
{
title: "浊度",
dataIndex: "lur",
key: "lur",
},
{
title: "排名",
key: "sort",
dataIndex: "sort",
},
],
data: [],
};
},
computed: {
sensorList() {
return this.$store.getters.sensorList
}
},
methods: {
},
async mounted() {},
};
</script>
<style>
</style>

View File

@ -1,131 +1,319 @@
<template> <template>
<div> <div style="height: calc(100vh - 110px)">
<a-row> <a-layout :theme="'light'" style="height: calc(100vh - 110px)">
<a-col :span="12"> <a-layout-sider :theme="'light'" :width="300" style="padding: 20px">
<a-range-picker <div style="text-align: center; font-size: 16px">选择站点</div>
separator="至" <a-divider />
:placeholder="['开始日期', '结束日期']" <a-tree
:defaultValue="datetimeRange" v-model="checkedKeys"
@change="dateRangeChange" checkable
></a-range-picker> :selected-keys="selectedKeys"
</a-col> :auto-expand-parent="autoExpandParent"
<a-col :span="12"> :tree-data="treeData"
<a-select @check="onSelect"
v-model="device" showLine
placeholder="请选择"
@change="onDeviceChange"
> >
<a-select-option <a-icon slot="switcherIcon" type="down" />
v-for="item in deviceList" </a-tree>
:key="item._id" </a-layout-sider>
<a-layout style="padding: 20px">
:value="item._id" <a-layout-header style="background: #fff; padding: 15px 0 5px 15px">
> {{item.device_name}} </a-select-option> <a-radio-group
</a-select> default-value="1-hours"
</a-col> button-style="solid"
</a-row> style="display: block; line-height: 32px"
<a-spin :spinning="loading"> @change="handleTimeRadioClick"
<a-row> >
<SZJCChart :dataRow="waterData" :labelMap="labelMap"></SZJCChart> <a-radio-button
</a-row> v-for="item in timeRadios"
</a-spin> :key="item.value"
:value="item.value"
>
{{ item.name }}
</a-radio-button>
</a-radio-group>
<a-radio-group
default-value="chart"
button-style="solid"
@change="handleRadioClick"
>
<a-radio-button
v-for="item in radios"
:key="item.value"
:value="item.value"
>
{{ item.name }}
</a-radio-button>
</a-radio-group>
<span style="margin-left: 30px"
><a-button @click="getData"> 刷新数据</span
>
</a-layout-header>
<a-layout-content style="padding: 20px 0 20px 0">
<a-spin :spinning="loading">
<a-table
v-if="currentType === 'table'"
:columns="columns"
:data-source="waterData"
bordered
:pagination="pagination"
size="middle"
@change="handleTableChange"
>
<span slot="device" slot-scope="record">
{{ deviceNames[record.device_id] }}
</span>
</a-table>
<div v-if="currentType === 'chart'">
<a-row v-for="chart in chartSettings" :key="chart.series" style="margin-bottom:30px;">
<base-index-chart
:height="'300px'"
:width="'100%'"
:rawData="waterData"
:setting="chart"
/>
</a-row>
</div>
<!-- -->
</a-spin>
</a-layout-content>
</a-layout>
</a-layout>
</div> </div>
</template> </template>
<script> <script>
import SZJCChart from "./SZJCChart"; // import SZJCChart from "./SZJCChart";
import { getData, URL_MAP } from "@/utils/http"; import { getData, URL_MAP } from "@/utils/http";
import Moment from 'moment' import Moment from "@/components/utils/moment.zh_cn";
import { processWater } from '@/utils/water' import { processWater } from "@/utils/water";
import BaseIndexChart from "../charts/BaseIndexChart.vue";
export default { export default {
components: { components: {
SZJCChart, BaseIndexChart
}, },
data() { data() {
return { return {
datetimeRange: [Moment().hour(0).minute(0).second(0), Moment()], checkedKeys: [],
update: false, expandedKeys: [],
device: "", autoExpandParent: true,
deviceList: [], selectedKeys: [],
deviceInstance: {}, treeData: [],
labelMap: {},
waterData: [], waterData: [],
pagination: {
total: 0,
pageSize: 10,
current: 0,
},
loading: false, loading: false,
update: false,
startTime: Moment().subtract(1, "hours"),
endTime: Moment(),
deviceNames: {},
timeRadios: [
{
value: "5-minutes",
name: "最近5分钟",
},
{
value: "15-minutes",
name: "最近15分钟",
},
{
value: "30-minutes",
name: "最近30分钟",
},
{
value: "1-hours",
name: "最近1小时",
},
{
value: "3-hours",
name: "最近3小时",
},
{
value: "6-hours",
name: "最近6小时",
},
{
value: "1-days",
name: "最近24小时",
},
],
radios: [
{
value: "table",
name: "表格",
},
{
value: "chart",
name: "图形",
},
],
currentType: "chart",
chartSettings: [],
}; };
}, },
watch: { computed: {
device: function (old, newVal) { sensorList() {
for (let i = 0; i < this.deviceList.length; i++) { return this.$store.getters.sensorList;
if (this.deviceList[i]._id === newVal) { },
this.deviceInstance = this.deviceList[i]; deviceList() {
break; return this.$store.getters.deviceList;
} },
} ruleList() {
return this.$store.getters.ruleList;
}, },
}, },
methods: { methods: {
async initSeting() { async initSeting() {
let sensors = (await getData(URL_MAP.SENSOR_LIST)).data // console.log(this.ruleList)
const labelMap = {} const columns = [
sensors.forEach(s => labelMap[s.param_key] = s.param_name) {
this.labelMap = labelMap title: "站点名",
}, key: "device_name",
scopedSlots: { customRender: "device" },
async initDevice() { },
const device_ = (await getData(URL_MAP.DEVICE_LIST)).data; {
try { title: "时间",
const device = device_.filter((item) => item.state == 1); dataIndex: "time_str",
this.deviceList = device; },
device.forEach((item) => { ];
Object.keys(this.labelMap).forEach((k) => { const ruleSlot = {};
if (item[k + "_factor"] === undefined) { this.ruleList.forEach((rule) => {
item[k + "_factor"] = 1; ruleSlot[rule.sensor.param_key] = {
} mmax: rule.allow_max,
}); mmin: rule.allow_min,
};
});
const chartSettings = [];
this.sensorList.forEach((sensor) => {
columns.push({
title: sensor.param_name,
children: [
{
title: sensor.uint || "-",
dataIndex: sensor.param_key,
customCell: (record, index) => {
if (
record[sensor.param_key] &&
record[sensor.param_key] >= ruleSlot[sensor.param_key].mmin &&
record[sensor.param_key] <= ruleSlot[sensor.param_key].mmax
)
return {};
else {
return {
style: { color: "#ff0000" },
};
}
},
// scopedSlots: { customRender: sensor.param_key },
},
],
}); });
this.device = device[0]._id chartSettings.push({
} catch (e) { title: sensor.param_name,
console.log(e); xAxis: "time",
} series: sensor.param_key,
seriesName: sensor.param_name,
seriesType: "line",
yRange: [
ruleSlot[sensor.param_key].mmin,
ruleSlot[sensor.param_key].mmax,
],
});
});
this.chartSettings = chartSettings;
// this.ruleSlot = ruleSlot;
this.columns = columns;
const deviceTree = [];
const deviceName = {};
this.checkedKeys = [];
this.deviceList.forEach((device) => {
deviceTree.push({
title: device.device_name,
key: device._id,
});
deviceName[device.device_id] = device.device_name;
this.checkedKeys.push(device._id);
});
this.deviceNames = deviceName;
this.treeData = [
{
title: "设备",
key: "all",
children: deviceTree,
},
];
}, },
async getData() { async getData() {
this.loading = true this.loading = true;
try { try {
const params = { const params = {
'data__collect_time__gte':this.datetimeRange[0].toDate().getTime(), timestamp__gte: this.startTime.toDate().getTime(),
'data__collect_time__lte':this.datetimeRange[1].toDate().getTime(), timestamp__lte: this.endTime.toDate().getTime(),
'device_id':this.device device_id__in: this.selectedKeys,
};
if (this.currentType === "table") {
params.page = this.pagination.current;
} }
let data = await getData(URL_MAP.WATER_LIST, params) if (this.currentType === "chart") {
// console.log(data) params.page = 0;
this.waterData = processWater(data.data, Object.keys(this.labelMap)) params.page_size = 1e13;
// console.log(this.waterData) }
let data = await getData(URL_MAP.WATER_LIST, params);
if (data.total === 0) {
this.waterData = [];
} else {
this.waterData = processWater(
data.data,
this.sensorList.map((s) => s.param_key)
);
}
this.pagination.total = data.total;
} catch (e) { } catch (e) {
console.log(e); console.log(e);
this.waterData = []; this.waterData = [];
} } finally {
finally{ this.loading = false;
this.loading = false
} }
}, },
handleTableChange(page, filters, sorter) {
onDeviceChange(value) { const pager = { ...this.pagination };
this.device = value pager.current = page.current;
this.pagination = pager;
this.getData(); this.getData();
}, },
dateRangeChange(dates, dateStrings) { dateRangeChange(dates, dateStrings) {
this.datetimeRange = dates; this.datetimeRange = dates;
console.log(this.datetimeRange) console.log(this.datetimeRange);
this.getData(); this.getData();
}, },
handleRadioClick(v) {
this.currentType = v.target.value;
},
handleTimeRadioClick(v) {
let now = Moment();
let pre = Moment();
let d = v.target.value.split("-");
pre = pre.subtract(parseInt(d[0]), d[1]);
this.startTime = pre;
this.endTime = now;
this.getData();
},
onSelect(keys) {
// console.log(keys)
this.selectedKeys = keys.filter((v) => v !== "all");
// console.log(this.selectedKeys)
},
}, },
async mounted() { async mounted() {
await this.initSeting(); await this.initSeting();
await this.initDevice();
this.getData(); this.getData();
}, },
}; };
@ -139,4 +327,7 @@ export default {
text-align: center; text-align: center;
line-height: 60px; line-height: 60px;
} }
.error-data {
color: rgb(255, 11, 2);
}
</style> </style>

View File

@ -0,0 +1,127 @@
<template>
<div style="height: calc(100vh - 110px)">
<a-layout :theme="'light'" style="height: calc(100vh - 110px)">
<a-layout-sider :theme="'light'" :width="300" style="padding: 20px">
<div style="text-align: center; font-size: 16px">选择站点</div>
<a-divider />
<a-tree
v-model="checkedKeys"
checkable
:selected-keys="selectedKeys"
:auto-expand-parent="true"
:tree-data="treeData"
@select="onSelect"
showLine
>
<a-icon slot="switcherIcon" type="down" />
</a-tree>
</a-layout-sider>
<a-layout style="padding: 20px">
<a-layout-header style="background: #fff; padding: 15px 0 5px 15px">
<span>
<a-range-picker
separator="至"
:placeholder="['开始日期', '结束日期']"
:defaultValue="datetimeRange"
@change="dateRangeChange"
></a-range-picker>
<a-button style="margin-left: 15px" type="primary" icon="search"
>查询</a-button
>
<a-button style="margin-left: 15px" type="primary" icon="download"
>导出Excel</a-button
>
</span>
</a-layout-header>
<a-layout-content style="padding: 20px 0 20px 0">
<a-table
:columns="columns"
:data-source="data"
bordered
size="middle"
/>
</a-layout-content>
</a-layout>
</a-layout>
</div>
</template>
<script>
export default {
data() {
return {
checkedKeys: ['all'],
autoExpandParent: true,
selectedKeys: [],
treeData: [],
datetimeRange: [],
columns: [],
data: [],
};
},
computed: {
sensorList() {
return this.$store.getters.sensorList;
},
deviceList() {
return this.$store.getters.deviceList;
},
},
methods: {
async fetch() {},
onSelect() {},
dateRangeChange() {},
},
mounted() {
const columns = [
{
title: "站点名",
dataIndex: "name",
},
{
title: "日期",
dataIndex: "time",
},
];
this.sensorList.forEach((sensor) => {
columns.push({
title: sensor.param_name + '('+(sensor.uint || "-") + ')',
children: [
{
title: '日均值',
dataIndex: sensor.param_key + '1',
},
{
title: '有效天数',
dataIndex: sensor.param_key + '2',
},
{
title: '超标天数',
dataIndex: sensor.param_key + '3',
},
],
});
});
this.columns = columns;
const deviceTree = [];
this.deviceList.forEach((device) => {
deviceTree.push({
title: device.device_name,
key: device._id,
});
});
this.treeData = [
{
title: "设备",
key: "all",
children: deviceTree,
},
];
},
};
</script>
<style>
</style>

View File

@ -0,0 +1,121 @@
<template>
<div style="height: calc(100vh - 110px)">
<a-layout :theme="'light'" style="height: calc(100vh - 110px)">
<a-layout-sider :theme="'light'" :width="300" style="padding: 20px">
<div style="text-align: center; font-size: 16px">选择站点</div>
<a-divider />
<a-tree
v-model="checkedKeys"
checkable
:selected-keys="selectedKeys"
:auto-expand-parent="true"
:tree-data="treeData"
@select="onSelect"
showLine
>
<a-icon slot="switcherIcon" type="down" />
</a-tree>
</a-layout-sider>
<a-layout style="padding: 20px">
<a-layout-header style="background: #fff; padding: 15px 0 5px 15px">
<span>
<a-range-picker
separator="至"
:placeholder="['开始日期', '结束日期']"
:defaultValue="datetimeRange"
@change="dateRangeChange"
></a-range-picker>
<a-button style="margin-left: 15px" type="primary" icon="search"
>查询</a-button
>
<a-button style="margin-left: 15px" type="primary" icon="download"
>导出Excel</a-button
>
</span>
</a-layout-header>
<a-layout-content style="padding: 20px 0 20px 0">
<a-table
:columns="columns"
:data-source="data"
bordered
size="middle"
/>
</a-layout-content>
</a-layout>
</a-layout>
</div>
</template>
<script>
export default {
data() {
return {
checkedKeys: ['all'],
autoExpandParent: true,
selectedKeys: [],
treeData: [],
datetimeRange: [],
columns: [],
data: [],
};
},
computed: {
sensorList() {
return this.$store.getters.sensorList;
},
deviceList() {
return this.$store.getters.deviceList;
},
},
methods: {
async fetch() {},
onSelect() {},
dateRangeChange() {},
},
mounted() {
const columns = [
{
title: "站点名",
dataIndex: "name",
},
{
title: "时间段",
dataIndex: "range",
},
{
title: '总超标(/次)',
dataIndex: "lur-count"
},
{
title: '单日最高超标(/次)',
dataIndex: "lur-count"
},
{
title: '单日最高超标(时间)',
dataIndex: "lur-count"
},
];
this.columns = columns;
const deviceTree = [];
this.deviceList.forEach((device) => {
deviceTree.push({
title: device.device_name,
key: device._id,
});
});
this.treeData = [
{
title: "设备",
key: "all",
children: deviceTree,
},
];
},
};
</script>
<style>
</style>

View File

@ -0,0 +1,217 @@
<template>
<div style="height: calc(100vh - 110px)">
<a-layout :theme="'light'" style="height: calc(100vh - 110px)">
<a-layout-sider :theme="'light'" :width="300" style="padding: 20px">
<div style="text-align: center; font-size: 16px">选择站点</div>
<a-divider />
<a-tree
v-model="checkedKeys"
checkable
:selected-keys="selectedKeys"
:auto-expand-parent="true"
:tree-data="treeData"
@select="onSelect"
showLine
>
<a-icon slot="switcherIcon" type="down" />
</a-tree>
</a-layout-sider>
<a-layout style="padding: 20px">
<a-layout-header style="background: #fff; padding: 15px 0 5px 15px">
<a-radio-group default-value="5_min" button-style="solid" style="display: block; line-height: 32px" @click="handleRadioClick">
<a-radio-button v-for="item in radios" :key="item.value" :value="item.value"> {{item.name}} </a-radio-button>
</a-radio-group>
<span>
<a-date-picker
:locale="locale"
v-model="startValue"
:disabled-date="disabledStartDate"
format="YYYY-MM-DD"
placeholder="开始日期"
@openChange="handleStartOpenChange"
/> -
<a-date-picker
:locale="locale"
v-model="endValue"
:disabled-date="disabledEndDate"
format="YYYY-MM-DD"
placeholder="结束日期"
/>
<a-button style="margin-left: 15px" type="primary" icon="search"
>查询</a-button
>
</span>
</a-layout-header>
<a-layout-content style="padding: 20px 0 20px 0">
<a-table
:columns="columns"
:data-source="data"
bordered
size="middle"
/>
</a-layout-content>
</a-layout>
</a-layout>
</div>
</template>
<script>
import Moment from '../utils/moment.zh_cn';
import locale from 'ant-design-vue/es/date-picker/locale/zh_CN';
export default {
data() {
return {
locale,
checkedKeys: ['all'],
autoExpandParent: true,
selectedKeys: [],
treeData: [],
startValue: Moment().subtract(1, 'days'),
endValue: Moment(),
endOpen: false,
columns: [],
data: [],
radios:[
{
value:'5_min',
name: '5分钟值',
rangeLimit: 7
},
{
value:'1_hour',
name: '小时值',
rangeLimit: 7
},
{
value:'day',
name: '日均值',
rangeLimit: 30
},
{
value:'month',
name: '月均值',
rangeLimit: 0
}
],
radioValueToRange:{
'5_min':7,'1_hour':7,'day':30,'month':0
},
currentRaido:'5_min'
};
},
computed: {
sensorList() {
return this.$store.getters.sensorList;
},
deviceList() {
return this.$store.getters.deviceList;
},
showTime() {
const showTimeValues = ['detail', '5_min', '1_hour']
return showTimeValues.includes(this.currentRaido)
}
},
watch: {
startValue(old, newVal) {
console.log(old)
let m = Moment(newVal).add(this.radioValueToRange[this.currentRaido], 'days')
if(this.radioValueToRange[this.currentRaido] === 0) {
return
}
if(this.endValue.valueOf() > m.valueOf()) {
this.endValue = m
}
}
},
methods: {
async fetch() {},
onSelect() {},
dateRangeChange() {},
handleRadioClick(v) {
this.currentRaido = v;
},
disabledStartDate(startValue) {
// const endValue = this.endValue;
// if (!startValue || !endValue) {
// return false;
// }
// let rangeDay = this.radioValueToRange[this.currentRaido]
// if(rangeDay>0)
// {
// let start_end_value = endValue.subtract(rangeDay, 'days')
// return (startValue.valueOf() < start_end_value.valueOf()) && (startValue.valueOf() > endValue.valueOf())
// }
return startValue.valueOf() > Moment().valueOf();
},
disabledEndDate(endValue) {
const startValue = this.startValue;
if (!endValue || !startValue) {
return false;
}
let rangeDay = this.radioValueToRange[this.currentRaido]
if(rangeDay>0)
{
let start_end_value = Moment(startValue).add(rangeDay, 'days')
// console.log(start_end_value.format('lll'), endValue.format('lll'), startValue.format('lll'))
return !( (endValue.valueOf() <= Moment().hour(24).valueOf()) &&
(endValue.valueOf() <= start_end_value.valueOf()) &&
(endValue.valueOf() >= startValue.valueOf()) )
}
return startValue.valueOf() > endValue.valueOf() || endValue.valueOf() > Moment().valueOf();
},
// handleStartOpenChange(open) {
// if (!open) {
// this.endOpen = true;
// }
// },
// handleEndOpenChange(open) {
// this.endOpen = open;
// },
},
mounted() {
const columns = [
{
title: "站点名",
dataIndex: "name",
},
{
title: "时间",
dataIndex: "time",
},
];
this.sensorList.forEach((sensor) => {
columns.push({
title: sensor.param_name,
children: [
{
title: sensor.uint || "-",
dataIndex: sensor.param_key,
},
],
});
});
this.columns = columns;
const deviceTree = [];
this.deviceList.forEach((device) => {
deviceTree.push({
title: device.device_name,
key: device._id,
});
});
this.treeData = [
{
title: "设备",
key: "all",
children: deviceTree,
},
];
},
};
</script>
<style>
</style>

View File

@ -0,0 +1,124 @@
<template>
<div style="height: calc(100vh - 110px)">
<a-layout :theme="'light'" style="height: calc(100vh - 110px)">
<a-layout-sider :theme="'light'" :width="300" style="padding: 20px">
<div style="text-align: center; font-size: 16px">选择站点</div>
<a-divider />
<a-tree
v-model="checkedKeys"
checkable
:selected-keys="selectedKeys"
:auto-expand-parent="true"
:tree-data="treeData"
@select="onSelect"
showLine
>
<a-icon slot="switcherIcon" type="down" />
</a-tree>
</a-layout-sider>
<a-layout style="padding: 20px">
<a-layout-header style="background: #fff; padding: 15px 0 5px 15px">
<span>
<a-range-picker
separator="至"
:placeholder="['开始日期', '结束日期']"
:defaultValue="datetimeRange"
@change="dateRangeChange"
></a-range-picker>
<a-button style="margin-left: 15px" type="primary" icon="search"
>查询</a-button
>
<a-button style="margin-left: 15px" type="primary" icon="download"
>导出Excel</a-button
>
</span>
</a-layout-header>
<a-layout-content style="padding: 20px 0 20px 0">
<a-table
:columns="columns"
:data-source="data"
bordered
size="middle"
/>
</a-layout-content>
</a-layout>
</a-layout>
</div>
</template>
<script>
export default {
data() {
return {
checkedKeys: ['all'],
autoExpandParent: true,
selectedKeys: [],
treeData: [],
datetimeRange: [],
columns: [],
data: [],
};
},
computed: {
sensorList() {
return this.$store.getters.sensorList;
},
deviceList() {
return this.$store.getters.deviceList;
},
},
methods: {
async fetch() {},
onSelect() {},
dateRangeChange() {},
},
mounted() {
const columns = [
{
title: "站点名",
dataIndex: "name",
},
{
title: "日期",
dataIndex: "time",
},
];
this.sensorList.forEach((sensor) => {
columns.push({
title: sensor.param_name,
children: [
{
title: sensor.uint || "-",
dataIndex: sensor.param_key,
},
],
});
});
columns.push({
title:'水质质量等级',
dataIndex:'level',
})
this.columns = columns;
const deviceTree = [];
this.deviceList.forEach((device) => {
deviceTree.push({
title: device.device_name,
key: device._id,
});
});
this.treeData = [
{
title: "设备",
key: "all",
children: deviceTree,
},
];
},
};
</script>
<style>
</style>

View File

@ -0,0 +1,170 @@
<template>
<div style="height: calc(100vh - 110px)">
<a-layout :theme="'light'" style="height: calc(100vh - 110px)">
<a-layout-sider :theme="'light'" :width="300" style="padding: 20px">
<div style="text-align: center; font-size: 16px">选择站点</div>
<a-divider />
<a-tree
v-model="checkedKeys"
checkable
:selected-keys="selectedKeys"
:auto-expand-parent="true"
:tree-data="treeData"
@select="onSelect"
showLine
>
<a-icon slot="switcherIcon" type="down" />
</a-tree>
</a-layout-sider>
<a-layout style="padding: 20px">
<a-layout-header style="background: #fff; padding: 15px 0 5px 15px">
<span>
<a-range-picker
separator="至"
:placeholder="['开始日期', '结束日期']"
:defaultValue="datetimeRange"
@change="dateRangeChange"
></a-range-picker>
<a-button style="margin-left: 15px" type="primary" icon="search"
>查询</a-button
>
<a-button style="margin-left: 15px" type="primary" icon="download"
>导出Excel</a-button
>
</span>
</a-layout-header>
<a-layout-content style="padding: 20px 0 20px 0">
<a-table
:columns="columns"
:data-source="data"
bordered
size="middle"
/>
</a-layout-content>
</a-layout>
</a-layout>
</div>
</template>
<script>
export default {
data() {
return {
checkedKeys: ['all'],
autoExpandParent: true,
selectedKeys: [],
treeData: [],
datetimeRange: [],
columns: [],
data: [],
};
},
computed: {
sensorList() {
return this.$store.getters.sensorList;
},
deviceList() {
return this.$store.getters.deviceList;
},
},
methods: {
async fetch() {},
onSelect() {},
dateRangeChange() {},
},
mounted() {
const columns = [
{
title: "站点名",
dataIndex: "name",
},
{
title: '优',
children:[
{
title: '优天数',
dataIndex:'good_days'
},
{
title: '比率',
dataIndex:'good_ratio'
}
]
},
{
title: '良',
children:[
{
title: '良天数',
dataIndex:'well_days'
},
{
title: '比率',
dataIndex:'well_ratio'
}
]
},
{
title: '轻微超标',
children:[
{
title: '轻微超标天数',
dataIndex:'small_days'
},
{
title: '比率',
dataIndex:'small_ratio'
}
]
},
{
title: '严重超标',
children:[
{
title: '严重超标天数',
dataIndex:'bad_days'
},
{
title: '比率',
dataIndex:'bad_ratio'
}
]
},
{
title: '合计',
children:[
{
title: '总天数',
dataIndex:'all_days'
},
{
title: '有效数据比例',
dataIndex:'all_ratio'
}
]
}
];
this.columns = columns;
const deviceTree = [];
this.deviceList.forEach((device) => {
deviceTree.push({
title: device.device_name,
key: device._id,
});
});
this.treeData = [
{
title: "设备",
key: "all",
children: deviceTree,
},
];
},
};
</script>
<style>
</style>

View File

@ -0,0 +1,133 @@
<template>
<div style="height: calc(100vh - 110px)">
<a-layout :theme="'light'" style="height: calc(100vh - 110px)">
<a-layout-sider :theme="'light'" :width="300" style="padding: 20px">
<div style="text-align: center; font-size: 16px">选择站点</div>
<a-divider />
<a-tree
v-model="checkedKeys"
checkable
:selected-keys="selectedKeys"
:auto-expand-parent="true"
:tree-data="treeData"
@select="onSelect"
showLine
>
<a-icon slot="switcherIcon" type="down" />
</a-tree>
</a-layout-sider>
<a-layout style="padding: 20px">
<a-layout-header style="background: #fff; padding: 15px 0 5px 15px">
<span>
<a-range-picker
separator="至"
:placeholder="['开始日期', '结束日期']"
:defaultValue="datetimeRange"
@change="dateRangeChange"
></a-range-picker>
<a-button style="margin-left: 15px" type="primary" icon="search"
>查询</a-button
>
<a-button style="margin-left: 15px" type="primary" icon="download"
>导出Excel</a-button
>
</span>
</a-layout-header>
<a-layout-content style="padding: 20px 0 20px 0">
<a-table
:columns="columns"
:data-source="data"
bordered
size="middle"
/>
</a-layout-content>
</a-layout>
</a-layout>
</div>
</template>
<script>
export default {
data() {
return {
checkedKeys: ['all'],
autoExpandParent: true,
selectedKeys: [],
treeData: [],
datetimeRange: [],
columns: [],
data: [],
};
},
computed: {
sensorList() {
return this.$store.getters.sensorList;
},
deviceList() {
return this.$store.getters.deviceList;
},
},
methods: {
async fetch() {},
onSelect() {},
dateRangeChange() {},
},
mounted() {
const columns = [
{
title: "站点名",
dataIndex: "name",
},
{
title: "时间段",
dataIndex: "range",
},
{
title: '检测值',
dataIndex: "lur-count"
},
{
title: '超标比率',
dataIndex: "lur-count"
},
{
title: '是否处理',
dataIndex: "lur-count"
},
{
title: '处理人',
dataIndex: "lur-count"
},
{
title: '处理时间',
dataIndex: "lur-count"
},
{
title: '处理概述',
dataIndex: "lur-count"
},
];
this.columns = columns;
const deviceTree = [];
this.deviceList.forEach((device) => {
deviceTree.push({
title: device.device_name,
key: device._id,
});
});
this.treeData = [
{
title: "设备",
key: "all",
children: deviceTree,
},
];
},
};
</script>
<style>
</style>

View File

@ -0,0 +1,115 @@
import Moment from 'moment'
Moment.locale('zh-cn', {
months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split(
'_'
),
monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split(
'_'
),
weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'),
weekdaysMin: '日_一_二_三_四_五_六'.split('_'),
longDateFormat: {
LT: 'HH:mm',
LTS: 'HH:mm:ss',
L: 'YYYY/MM/DD',
LL: 'YYYY年M月D日',
LLL: 'YYYY年M月D日Ah点mm分',
LLLL: 'YYYY年M月D日ddddAh点mm分',
l: 'YYYY/M/D',
ll: 'YYYY年M月D日',
lll: 'YYYY年M月D日 HH:mm',
llll: 'YYYY年M月D日dddd HH:mm',
},
meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
meridiemHour: function (hour, meridiem) {
if (hour === 12) {
hour = 0;
}
if (meridiem === '凌晨' || meridiem === '早上' || meridiem === '上午') {
return hour;
} else if (meridiem === '下午' || meridiem === '晚上') {
return hour + 12;
} else {
// '中午'
return hour >= 11 ? hour : hour + 12;
}
},
meridiem: function (hour, minute, isLower) {
var hm = hour * 100 + minute;
if (hm < 600) {
return '凌晨';
} else if (hm < 900) {
return '早上';
} else if (hm < 1130) {
return '上午';
} else if (hm < 1230) {
return '中午';
} else if (hm < 1800) {
return '下午';
} else {
return '晚上';
}
},
calendar: {
sameDay: '[今天]LT',
nextDay: '[明天]LT',
nextWeek: function (now) {
if (now.week() !== this.week()) {
return '[下]dddLT';
} else {
return '[本]dddLT';
}
},
lastDay: '[昨天]LT',
lastWeek: function (now) {
if (this.week() !== now.week()) {
return '[上]dddLT';
} else {
return '[本]dddLT';
}
},
sameElse: 'L',
},
dayOfMonthOrdinalParse: /\d{1,2}(日|月|周)/,
ordinal: function (number, period) {
switch (period) {
case 'd':
case 'D':
case 'DDD':
return number + '日';
case 'M':
return number + '月';
case 'w':
case 'W':
return number + '周';
default:
return number;
}
},
relativeTime: {
future: '%s后',
past: '%s前',
s: '几秒',
ss: '%d 秒',
m: '1 分钟',
mm: '%d 分钟',
h: '1 小时',
hh: '%d 小时',
d: '1 天',
dd: '%d 天',
w: '1 周',
ww: '%d 周',
M: '1 个月',
MM: '%d 个月',
y: '1 年',
yy: '%d 年',
},
week: {
// GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
dow: 1, // Monday is the first day of the week.
doy: 4, // The week that contains Jan 4th is the first week of the year.
},
});
export default Moment

View File

@ -1,4 +1,5 @@
import axios from "axios"; import axios from "axios";
import qs from 'qs'
const backendUrl = '/backend/' const backendUrl = '/backend/'
const geoServerUrl = '/geoserver/' const geoServerUrl = '/geoserver/'
@ -20,17 +21,18 @@ export const URL_MAP = {
CHANGE_PASSWD: 'current/chpass/', CHANGE_PASSWD: 'current/chpass/',
RESET_PASSWD: 'passwd/reset/', RESET_PASSWD: 'passwd/reset/',
POSITION_LIST: 'position/', POSITION_LIST: 'position/',
POSITION_DETAIL: 'position/detail/' POSITION_DETAIL: 'position/detail/',
RULES_LIST: 'parammanager/',
RULES_DETAIL: 'parammanager/detail/'
} }
export async function login(username, passwd, uuid, capthe) export async function login(username, passwd, uuid, capthe) {
{
let response = await instance.post('auth', { let response = await instance.post('auth', {
'username':username, 'username': username,
'password':passwd, 'password': passwd,
'uuid':uuid, 'uuid': uuid,
'captch':capthe 'captch': capthe
}) })
let data = await response.data let data = await response.data
let token = data.access_token let token = data.access_token
@ -46,14 +48,17 @@ export function setAuth(auth) {
instance.defaults.headers['Authorization'] = 'JWT ' + auth; instance.defaults.headers['Authorization'] = 'JWT ' + auth;
} }
export async function logout() export async function logout() {
{
instance.defaults.headers['Authorization'] = '' instance.defaults.headers['Authorization'] = ''
} }
export async function getData(url, json) { export async function getData(url, json) {
let response = await instance.get(url, { let response = await instance.get(url, {
params: json params: json
,
paramsSerializer: function (params) {
return qs.stringify(params, { arrayFormat: 'repeat' })
}
}) })
let data = await response.data let data = await response.data
@ -63,7 +68,7 @@ export async function getData(url, json) {
export async function postJSON(url, json) { export async function postJSON(url, json) {
const headerJSON = { const headerJSON = {
"Content-Type": "application/json" "Content-Type": "application/json"
}; };
let response = await instance.post(url, JSON.stringify(json), { headers: headerJSON }) let response = await instance.post(url, JSON.stringify(json), { headers: headerJSON })
let data = await response.data let data = await response.data
return data return data
@ -72,7 +77,7 @@ export async function postJSON(url, json) {
export async function putJSON(url, json) { export async function putJSON(url, json) {
const headerJSON = { const headerJSON = {
"Content-Type": "application/json" "Content-Type": "application/json"
}; };
let response = await instance.put(url, JSON.stringify(json), { headers: headerJSON }) let response = await instance.put(url, JSON.stringify(json), { headers: headerJSON })
let data = await response.data let data = await response.data
return data return data
@ -81,7 +86,7 @@ export async function putJSON(url, json) {
export async function patchJSON(url, json) { export async function patchJSON(url, json) {
const headerJSON = { const headerJSON = {
"Content-Type": "application/json" "Content-Type": "application/json"
}; };
let response = await instance.patch(url, JSON.stringify(json), { headers: headerJSON }) let response = await instance.patch(url, JSON.stringify(json), { headers: headerJSON })
let data = await response.data let data = await response.data
return data return data
@ -90,8 +95,8 @@ export async function patchJSON(url, json) {
export async function deleteJSON(url, json) { export async function deleteJSON(url, json) {
const headerJSON = { const headerJSON = {
"Content-Type": "application/json" "Content-Type": "application/json"
}; };
let response = await instance.delete(url, { data:json }) let response = await instance.delete(url, { data: json })
let data = await response.data let data = await response.data
return data return data
} }

View File

@ -9,6 +9,12 @@ Vue.use(VueRouter)
const funcRoutes = [ const funcRoutes = [
{ path:'map-layout', name:'map-layout', component: ()=>import(/* webpackChunkName: "map-layout" */ '@/components/map/Index' ) }, { path:'map-layout', name:'map-layout', component: ()=>import(/* webpackChunkName: "map-layout" */ '@/components/map/Index' ) },
{ path:'detail-search', name:'detail-search', component: ()=>import(/* webpackChunkName: "detail-search" */ '@/components/search/Index' ) }, { path:'detail-search', name:'detail-search', component: ()=>import(/* webpackChunkName: "detail-search" */ '@/components/search/Index' ) },
{ path:'means', name:'means', component: ()=>import(/* webpackChunkName: "means" */ '@/components/search/Means' ) },
{ path:'qa-day', name:'qa-day', component: ()=>import(/* webpackChunkName: "qa-day" */ '@/components/search/QaDay' ) },
{ path:'lur-mean', name:'lur-mean', component: ()=>import(/* webpackChunkName: "lur-mean" */ '@/components/search/LurMean' ) },
{ path:'qa-sts', name:'qa-sts', component: ()=>import(/* webpackChunkName: "qa-sts" */ '@/components/search/QaSTS' ) },
{ path:'lur-sts', name:'lur-sts', component: ()=>import(/* webpackChunkName: "lur-sts" */ '@/components/search/LurSTS' ) },
{ path:'yl-sts', name:'yl-sts', component: ()=>import(/* webpackChunkName: "yl-sts" */ '@/components/search/YLSTS' ) },
{ path:'admin', name:'admin', component: ()=>import(/* webpackChunkName: "admin" */ '@/components/admin/Index' ) }, { path:'admin', name:'admin', component: ()=>import(/* webpackChunkName: "admin" */ '@/components/admin/Index' ) },
{ path:'analysis', name:'analysis', component: ()=>import(/* webpackChunkName: "analysis" */ '@/components/analysis/Index' ) }, { path:'analysis', name:'analysis', component: ()=>import(/* webpackChunkName: "analysis" */ '@/components/analysis/Index' ) },
] ]
@ -65,5 +71,5 @@ router.beforeEach((to, from, next) => {
next() next()
} }
}) })
console.log(router)
export default router export default router

View File

@ -27,7 +27,8 @@ const store = new Vuex.Store({
cacheData:{}, cacheData:{},
deviceList:[], deviceList:[],
projectList:[], projectList:[],
sensorList:[] sensorList:[],
ruleList:[]
}, },
mutations: { mutations: {
phone(state, o) { phone(state, o) {
@ -52,6 +53,9 @@ const store = new Vuex.Store({
}, },
sensorList(state, o) { sensorList(state, o) {
state.sensorList = o state.sensorList = o
},
ruleList(state, o) {
state.ruleList = o
} }
}, },
getters: { getters: {
@ -120,6 +124,9 @@ const store = new Vuex.Store({
sensorList(state) { sensorList(state) {
return state.sensorList return state.sensorList
}, },
ruleList(state) {
return state.ruleList
},
username(state, getters) { username(state, getters) {
if (getters.isLogin) if (getters.isLogin)
{ {

View File

@ -1,3 +1,5 @@
import Moment from 'com/utils/moment.zh_cn'
export function processWater(water, label){ export function processWater(water, label){
const waters = [] const waters = []
@ -8,9 +10,10 @@ export function processWater(water, label){
if(!!w.data) { if(!!w.data) {
n[key] = w.data[key] n[key] = w.data[key]
} }
} ) } )
n['time'] = new Date(w.data.collect_time) n['key'] = w._id.$oid
n['time_str'] = Moment(w.timestamp).format('lll')
n['time'] = Moment(w.timestamp).toDate()
waters.push(n) waters.push(n)
} ) } )
return waters return waters

View File

@ -1,4 +1,5 @@
const path = require('path') const path = require('path')
const { env } = require('process')
module.exports = { module.exports = {
publicPath: '/', publicPath: '/',
@ -10,7 +11,8 @@ module.exports = {
proxy: { proxy: {
'/backend': { '/backend': {
// target: 'http://223.75.53.208:8081', // target: 'http://223.75.53.208:8081',
target: 'http://121.36.155.145:10025/', // target: 'http://121.36.155.145:10025/',
target: 'http://127.0.0.1:7788/'
} }
} }
}, },
@ -31,7 +33,7 @@ module.exports = {
'com': path.resolve(__dirname, 'src', 'components') 'com': path.resolve(__dirname, 'src', 'components')
} }
}, },
externals: { externals: true ? {
'vue': 'Vue', 'vue': 'Vue',
'vuex':'Vuex', 'vuex':'Vuex',
'antd': 'antd', 'antd': 'antd',
@ -41,6 +43,13 @@ module.exports = {
'axios': 'axios', 'axios': 'axios',
'v-charts':'VCharts', 'v-charts':'VCharts',
'ol':'ol' 'ol':'ol'
}: {
'antd': 'antd',
'ol':'ol',
'v-charts':'VCharts',
"moment": "moment",
"md5": "js-md5",
'axios': 'axios',
}, },
module: { module: {