1. 添加后台管理实现:对项目、设备、人员、传感器的增删改查

2. 集成数据查询功能
This commit is contained in:
王铜 2020-11-29 13:49:42 +08:00
parent 40149cb58c
commit 666460a0d9
18 changed files with 1212 additions and 162 deletions

View File

@ -11,7 +11,9 @@
"ant-design-vue": "^1.7.0",
"axios": "^0.21.0",
"core-js": "^3.6.5",
"echarts": "^4.9.0",
"ol": "^6.4.3",
"v-charts": "^1.19.0",
"vue": "^2.6.11",
"vue-router": "^3.4.8",
"vuex": "^3.5.1"

View File

@ -2,8 +2,8 @@
<a-layout>
<a-layout-header style="height: 110px;">
<a-row>
<a-col :span="12">
水质监测平台
<a-col :span="12" style="color:white;">
<span class="w-title">水质监测平台</span>
</a-col>
<a-col :span="2" :offset="10">
<a-button icon="logout" @click="logout" >登出</a-button>
@ -11,34 +11,27 @@
</a-row>
<a-row>
<a-menu v-model="current" mode="horizontal" theme="dark" @click="handleMenuClick">
<a-menu-item key="map">
<a-menu-item key="map-layout">
<icon-font type="wt-iconmap" />主页
</a-menu-item>
<a-sub-menu>
<span slot="title" class="submenu-title-wrapper"
><icon-font type="wt-iconsearch" />数据查询</span
>
<a-menu-item-group title="Item 1">
<a-menu-item key="search"> 详细信息 </a-menu-item>
<a-menu-item key="setting:2"> Option 2 </a-menu-item>
</a-menu-item-group>
<a-menu-item-group title="Item 2">
<a-menu-item key="setting:3"> Option 3 </a-menu-item>
<a-menu-item key="setting:4"> Option 4 </a-menu-item>
</a-menu-item-group>
<a-menu-item key="detail-search"> <icon-font type="wt-iconxiangxi" />详细数据 </a-menu-item>
</a-sub-menu>
<a-menu-item key="analysis">
<icon-font type="wt-iconmap" />图形分析
<icon-font type="wt-iconfenxi" />图形分析
</a-menu-item>
<a-menu-item key="admin" v-if="isAdmin">
<icon-font type="wt-iconmap" />后台管理
<icon-font type="wt-iconhoutai" />后台管理
</a-menu-item>
</a-menu>
</a-row>
</a-layout-header>
<a-layout-content>
<keep-alive >
<keep-alive>
<component :is="currentComp"/>
</keep-alive>
</a-layout-content>
@ -49,7 +42,9 @@
import IconFont from '@/components/icon'
import MapLayout from '@/components/map/Index'
import Search from '@/components/search/Index'
import DetailSearch from '@/components/search/Index'
import Analysis from '@/components/analysis/Index'
import Logs from '@/components/logs/Index'
import Admin from '@/components/admin/Index'
import { logout } from '@/utils/http'
@ -57,13 +52,14 @@ export default {
components: {
'icon-font':IconFont,
'map-layout':MapLayout,
'search':Search,
'detail-search':DetailSearch,
'analysis':Analysis,
// 'logs':Logs,
'admin':Admin
},
data() {
return {
current:['map'],
current:['map-layout'],
currentComp: 'map-layout'
};
},
@ -95,4 +91,11 @@ export default {
.ant-layout-content {
height: calc(100vh - 110px);
}
.w-title{
color: white;
line-height: 50px;
font-size: 24px;
font-weight: 600;
}
</style>

View File

@ -20,6 +20,10 @@
<a-icon type="inbox" />
<span>项目</span>
</a-menu-item>
<a-menu-item key="user-table">
<a-icon type="user" />
<span>人员</span>
</a-menu-item>
</a-menu>
</a-layout-sider>
<a-layout>
@ -35,13 +39,14 @@
import SensorTable from "./tables/SensorTable";
import DeviceTable from './tables/DeviceTable';
import ProjectTable from './tables/ProjectTable';
import UserTable from './tables/UserTable';
import { getData, URL_MAP } from "@/utils/http";
export default {
components: {
'sensor-table':SensorTable,
'device-table':DeviceTable,
'project-table':ProjectTable
'project-table':ProjectTable,
'user-table':UserTable
},
data() {
return {

View File

@ -0,0 +1,136 @@
<template>
<div>
<a-form-model :model="form">
<a-form-model-item label="设备ID">
<a-input :disabled="isCreate" v-model="form._id" />
</a-form-model-item>
<a-form-model-item label="设备名称">
<a-input v-model="form.device_name" />
</a-form-model-item>
<a-form-model-item label="设备型号">
<a-input v-model="form.device_uint" />
</a-form-model-item>
<a-form-model-item label="是否公开">
<a-switch v-model="form.isanyone" />
</a-form-model-item>
<a-form-model-item label="传感器">
<a-select
mode="multiple"
v-model="form.sensors"
@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="所属项目">
<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 :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: "",
device_name: "",
device_uint: "",
sensors: [],
project:'',
state: 0,
isanyone: false,
position: "",
},
sensors: [],
projects: [],
fetching: false,
isCreate: true,
};
},
props: {
detail: Object,
},
methods: {
onSubmit() {
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;
}
},
},
mounted() {
for (let key in this.form) {
this.form[key] = this.detail[key];
}
this.isCreate = !!this.form._id
if(this.detail.sensors) {
this.form.sensors = this.form.sensors.map((v) => v._id);
this.sensors = this.detail.sensors
}
else{
this.form.sensors = []
this.sensors = []
}
if(this.detail.project) {
this.form.project = this.form.project._id
this.projects = [this.detail.project,];
}else{
this.form.project = null
this.projects = []
}
},
};
</script>
<style>
</style>

View File

@ -0,0 +1,118 @@
<template>
<div>
<a-form-model :model="form">
<a-form-model-item label="项目名">
<a-input v-model="form.name" />
</a-form-model-item>
<a-form-model-item label="管理员">
<a-select
mode="multiple"
v-model="form.managers"
@search="searchManagers"
: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 managers" :key="u._id">
{{u.name || u.username}}
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="普通用户">
<a-select
mode="multiple"
v-model="form.normals"
@search="searchNormals"
: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 normals" :key="u._id">
{{u.name || u.username}}
</a-select-option>
</a-select>
</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:'',
name:'',
managers:[],
normals:[]
},
managers: [],
normals: [],
fetching: false,
}
},
props: {
detail:Object
},
methods: {
onSubmit() {
this.$emit('ok', this.form)
},
async searchManagers(value) {
this.fetching = true
try{
let data = await getData(URL_MAP.USER_LIST, {
'username__icontains':name,
'permission__gte':0x0ff
})
this.managers = mergeList(data.data, this.managers)
}
catch(e) {
console.log(e)
}finally{
this.fetching = false
}
},
async searchNormals(name, permission) {
this.fetching = true
try{
let data = await getData(URL_MAP.USER_LIST, {
'username__icontains':name,
'permission__gte':0x00f
})
this.normals = mergeList(data.data, this.normals)
}
catch(e) {
console.log(e)
}finally{
this.fetching = false
}
},
},
mounted() {
for(let key in this.form) {
this.form[key] = this.detail[key]
}
this.form.managers = 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

@ -0,0 +1,57 @@
<template>
<div>
<a-form-model :model="form">
<a-form-model-item label="名称">
<a-input v-model="form.name" />
</a-form-model-item>
<a-form-model-item label="参数名">
<a-input v-model="form.param_name" />
</a-form-model-item>
<a-form-model-item label="参数key">
<a-input v-model="form.param_key" />
</a-form-model-item>
<a-form-model-item label="单位">
<a-input v-model="form.uint" />
</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>
export default {
data() {
return {
form: {
_id: "",
name: "",
param_name: "",
param_key: '',
uint:''
},
};
},
props: {
detail: Object,
},
methods: {
onSubmit() {
this.$emit("ok", this.form);
}
},
mounted() {
for (let key in this.form) {
this.form[key] = this.detail[key];
}
},
};
</script>
<style>
</style>

View File

@ -0,0 +1,73 @@
<template>
<div>
<a-form-model :model="form">
<a-form-model-item label="用户名">
<a-input v-model="form.username" />
</a-form-model-item>
<a-form-model-item label="权限">
<a-select
v-model="form.permission"
>
<a-select-option v-for="p in permissionMaps" :key="p.key">
{{p.name}}
</a-select-option>
</a-select>
</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>
export default {
data() {
return {
form: {
_id: "",
username: "",
permission: 0x000,
},
permissionMaps: [
{
key:0xfff,
name: '超级管理员',
},
{
key:0x0ff,
name: '项目管理员',
},
{
key:0x00f,
name: '普通用户',
},
{
key:0x000,
name: '游客',
}
],
};
},
props: {
detail: Object,
},
methods: {
onSubmit() {
this.$emit("ok", this.form);
}
},
mounted() {
for (let key in this.form) {
this.form[key] = this.detail[key];
}
},
};
</script>
<style>
</style>

View File

@ -1,5 +1,8 @@
<template>
<div>
<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"
@ -7,107 +10,166 @@
:rowKey="idName"
@change="handleTableChange"
>
<span slot="anyone" slot-scope="isanyone">
<a-tag v-if="isanyone">
</a-tag>
<a-tag v-else>
</a-tag>
</span>
<span slot="project" slot-scope="project">
<a-tag v-if="project !== null">
{{ project.name}}
</a-tag>
</span>
<span slot="action" slot-scope="text, 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>
<span slot="anyone" slot-scope="isanyone">
<a-tag v-if="isanyone"> </a-tag>
<a-tag v-else> </a-tag>
</span>
<span slot="project" slot-scope="project">
<a-tag v-if="project !== null">
{{ project.name }}
</a-tag>
</span>
<span slot="action" slot-scope="text, 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"
>
<device-detail
:detail="record"
@ok="handleOk"
@cancle="visible = false"
/>
</a-modal>
</div>
</template>
<script>
import { getData, URL_MAP } from '@/utils/http'
import { deleteJSON, patchJSON, postJSON, getData, URL_MAP } from "@/utils/http";
import DeviceDetail from "../forms/DeviceDetail.vue";
export default {
components: { DeviceDetail },
data() {
return {
tableRows: [],
tableCols: [
{
dataIndex: '_id',
key: '_id',
title: '设备ID'
dataIndex: "_id",
key: "_id",
title: "设备ID",
},
{
dataIndex: 'device_name',
key: 'device_name',
title: '设备名称'
dataIndex: "device_name",
key: "device_name",
title: "设备名称",
},
{
dataIndex: 'device_uint',
key: 'device_uint',
title: '设备型号'
dataIndex: "device_uint",
key: "device_uint",
title: "设备型号",
},
{
dataIndex: 'isanyone',
key: 'isanyone',
title: '是否公开',
scopedSlots: { customRender: 'anyone' },
dataIndex: "isanyone",
key: "isanyone",
title: "是否公开",
scopedSlots: { customRender: "anyone" },
},
{
dataIndex: 'project',
key: 'project',
title: '所属项目',
scopedSlots: { customRender: 'project' }
dataIndex: "project",
key: "project",
title: "所属项目",
scopedSlots: { customRender: "project" },
},
{
key: 'action',
title: '操作',
scopedSlots: { customRender: 'action' },
}
key: "action",
title: "操作",
scopedSlots: { customRender: "action" },
},
],
pages: {},
loading: false,
listUrl: URL_MAP.DEVICE_LIST,
detailUrl: URL_MAP.DEVICE_DETAIL,
idName: '_id'
idName: "_id",
visible: false,
record: {},
isCreate: false,
};
},
methods: {
handleTableChange(pagination, filters, sorter) {
console.log(pagination)
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
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) {
console.log(record)
// console.log(record)
this.visible = true;
this.record = record;
},
handleDelete(record) {
console.log(record)
async handleDelete(record) {
this.loading = true
try{
await deleteJSON(URL_MAP.DEVICE_DETAIL, { '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["_id"];
console.log(form)
let data = await patchJSON(URL_MAP.DEVICE_DETAIL, form);
} else {
form["id"] = form["_id"];
let data = await postJSON(URL_MAP.DEVICE_LIST, 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 = {
'_id':'',
'device_name':'',
'device_uint':'',
'isanyone':false,
'state':0,
'sensors':[],
'project':null
}
}
},
mounted() {
this.fetch({})
}
this.fetch({});
},
};
</script>

View File

@ -1,5 +1,8 @@
<template>
<div>
<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"
@ -7,95 +10,155 @@
:rowKey="idName"
@change="handleTableChange"
>
<span slot="managers" slot-scope="managers">
<a-tag v-for="item in managers" :key="item._id">
{{ item.name}}
</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="text, 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>
<span slot="managers" slot-scope="managers">
<a-tag v-for="item in managers" :key="item._id">
{{ item.name }}
</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="text, 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"
>
<project-detail
:detail="record"
@ok="handleOk"
@cancle="visible = false"
/>
</a-modal>
</div>
</template>
<script>
import { getData, URL_MAP } from '@/utils/http'
import { deleteJSON, getData, patchJSON, postJSON, URL_MAP } from "@/utils/http";
import ProjectDetail from "../forms/ProjectDetail.vue";
export default {
components: {
ProjectDetail,
},
data() {
return {
tableRows: [],
tableCols: [
{
dataIndex: 'name',
key: 'name',
title: '名称'
dataIndex: "name",
key: "name",
title: "名称",
},
{
dataIndex: 'managers',
key: 'managers',
title: '管理员',
scopedSlots: { customRender: 'managers' }
dataIndex: "managers",
key: "managers",
title: "管理员",
scopedSlots: { customRender: "managers" },
},
{
dataIndex: 'normals',
key: 'normals',
title: '普通用户',
scopedSlots: { customRender: 'normals' }
dataIndex: "normals",
key: "normals",
title: "普通用户",
scopedSlots: { customRender: "normals" },
},
{
key: 'action',
title: '操作',
scopedSlots: { customRender: 'action' },
}
key: "action",
title: "操作",
scopedSlots: { customRender: "action" },
},
],
pages: {},
loading: false,
listUrl: URL_MAP.PROJECT_LIST,
detailUrl: URL_MAP.PROJECT_DETAIL,
idName: '_id'
idName: "_id",
visible: false,
record: {},
isCreate: false,
};
},
methods: {
handleTableChange(pagination, filters, sorter) {
console.log(pagination)
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{
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)
await deleteJSON(URL_MAP.PROJECT_DETAIL, { 'id': record._id })
await this.fetch({})
}catch(e)
{
console.log(e)
}finally{
}
finally{
this.loading = false
}
},
handleDetail(record) {
console.log(record)
async handleOk(form) {
this.visible = false;
this.loading = true;
try {
if (!this.isCreate) {
form["id"] = form["_id"];
let data = await patchJSON(URL_MAP.PROJECT_DETAIL, form);
} else {
form["_id"] = undefined;
let data = await postJSON(URL_MAP.PROJECT_LIST, 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 = {
name: null,
managers: [],
normals: [],
};
},
handleDelete(record) {
console.log(record)
}
},
mounted() {
this.fetch({})
}
this.fetch({});
},
};
</script>

View File

@ -1,5 +1,8 @@
<template>
<div>
<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"
@ -13,12 +16,27 @@
<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"
>
<sensor-detai
:detail="record"
@ok="handleOk"
@cancle="visible = false"
/>
</a-modal>
</div>
</template>
<script>
import { getData, URL_MAP } from '@/utils/http'
import { deleteJSON, patchJSON, postJSON, getData, URL_MAP } from '@/utils/http'
import SensorDetai from '../forms/SensorDetai.vue';
export default {
components: { SensorDetai },
data() {
return {
tableRows: [],
@ -38,6 +56,11 @@ export default {
key: 'param_key',
title: '参数key'
},
{
dataIndex: 'uint',
key: 'uint',
title: '单位'
},
{
key: 'action',
title: '操作',
@ -48,7 +71,10 @@ export default {
loading: false,
listUrl: URL_MAP.SENSOR_LIST,
detailUrl: URL_MAP.SENSOR_DETAIL,
idName: '_id'
idName: '_id',
visible: false,
record: {},
isCreate: false,
};
},
methods: {
@ -73,10 +99,53 @@ export default {
},
handleDetail(record) {
console.log(record)
// console.log(record)
this.visible = true;
this.record = record;
},
handleDelete(record) {
console.log(record)
async handleDelete(record) {
this.loading = true
try{
await deleteJSON(URL_MAP.SENSOR_DETAIL, { '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["_id"];
let data = await patchJSON(URL_MAP.SENSOR_DETAIL, form);
} else {
form["id"] = undefined;
form['_id'] = undefined;
let data = await postJSON(URL_MAP.SENSOR_LIST, 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 = {
'_id':'',
'param_name':'',
'param_key':'',
'name':'',
'uint':''
}
}
},
mounted() {

View File

@ -0,0 +1,157 @@
<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="permission" slot-scope="permission">
<a-tag >
{{ permissionMap[permission] }}
</a-tag>
</span>
<span slot="action" slot-scope="text, 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"
>
<user-detail
:detail="record"
@ok="handleOk"
@cancle="visible = false"
/>
</a-modal>
</div>
</template>
<script>
import { deleteJSON, getData, patchJSON, postJSON, URL_MAP } from '@/utils/http'
import UserDetail from '../forms/UserDetail.vue';
export default {
components: { UserDetail },
data() {
return {
tableRows: [],
tableCols: [
{
dataIndex: 'username',
key: 'username',
title: '用户名'
},
{
dataIndex: 'permission',
key: 'permission',
title: '用户权限',
scopedSlots: { customRender: 'permission' },
},
{
key: 'action',
title: '操作',
scopedSlots: { customRender: 'action' },
}
],
permissionMap: {
0xfff:'超级管理员',
0x0ff:'项目管理员',
0x00f:'普通用户',
0x000:'游客'
},
pages: {},
loading: false,
listUrl: URL_MAP.USER_LIST,
detailUrl: URL_MAP.DEVICE_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(URL_MAP.USER_DETAIL, { '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["_id"];
let data = await patchJSON(URL_MAP.USER_DETAIL, form);
} else {
form["_id"] = undefined;
let data = await postJSON(URL_MAP.USER_LIST, 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 = {
_id:'',
username: '',
permission: 0x000
};
},
},
mounted() {
this.fetch({})
}
};
</script>
<style>
</style>

View File

@ -1,7 +1,7 @@
import { Icon } from 'ant-design-vue';
const IconFont = Icon.createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_2205869_701srfkqc5.js', // generated by iconfont.cn
scriptUrl: '//at.alicdn.com/t/font_2205869_ablhiuxmevf.js', // generated by iconfont.cn
});
export default IconFont

View File

@ -1,18 +1,142 @@
<template>
<div>
<a-layout>
<a-layout-sider>Sider</a-layout-sider>
<a-layout-content>Content</a-layout-content>
</a-layout>
<a-row>
<a-col :span="12">
<a-range-picker
separator="至"
:placeholder="['开始日期', '结束日期']"
:defaultValue="datetimeRange"
@change="dateRangeChange"
></a-range-picker>
</a-col>
<a-col :span="12">
<a-select
v-model="device"
placeholder="请选择"
@change="onDeviceChange"
>
<a-select-option
v-for="item in deviceList"
:key="item._id"
:value="item._id"
> {{item.device_name}} </a-select-option>
</a-select>
</a-col>
</a-row>
<a-spin :spinning="loading">
<a-row>
<SZJCChart :dataRow="waterData" :labelMap="labelMap"></SZJCChart>
</a-row>
</a-spin>
</div>
</template>
<script>
import SZJCChart from "./SZJCChart";
import { getData, URL_MAP } from "@/utils/http";
import Moment from 'moment'
import { processWater } from '@/utils/water'
export default {
components: {
SZJCChart,
},
data() {
return {
datetimeRange: [Moment('2020-10-01'), Moment('2020-12-31')],
update: false,
device: "",
deviceList: [],
deviceInstance: {},
labelMap: {},
waterData: [],
loading: false,
};
},
watch: {
device: function (old, newVal) {
for (let i = 0; i < this.deviceList.length; i++) {
if (this.deviceList[i]._id === newVal) {
this.deviceInstance = this.deviceList[i];
break;
}
}
},
},
methods: {
async initSeting() {
let sensors = (await getData(URL_MAP.SENSOR_LIST)).data
const labelMap = {}
sensors.forEach(s => labelMap[s.param_key] = s.param_name)
this.labelMap = labelMap
},
}
async initDevice() {
const device_ = (await getData(URL_MAP.DEVICE_LIST)).data;
try {
const device = device_.filter((item) => item.state == 1);
this.deviceList = device;
device.forEach((item) => {
Object.keys(this.labelMap).forEach((k) => {
if (item[k + "_factor"] === undefined) {
item[k + "_factor"] = 1;
}
});
});
this.device = device[0]._id
} catch (e) {
console.log(e);
}
},
async getData() {
this.loading = true
try {
const params = {
'data__collect_time__gte':this.datetimeRange[0].toDate().getTime(),
'data__collect_time__lte':this.datetimeRange[1].toDate().getTime(),
'device_id':this.device
}
let data = await getData(URL_MAP.WATER_LIST, params)
// console.log(data)
this.waterData = processWater(data.data, Object.keys(this.labelMap))
// console.log(this.waterData)
} catch (e) {
console.log(e);
this.waterData = [];
}
finally{
this.loading = false
}
},
onDeviceChange(value) {
this.device = value
this.getData();
},
dateRangeChange(dates, dateStrings) {
this.datetimeRange = dates;
console.log(this.datetimeRange)
this.getData();
},
},
async mounted() {
await this.initSeting();
await this.initDevice();
this.getData();
},
};
</script>
<style>
.el-header,
.el-footer {
background-color: #b3c0d1;
color: #333;
text-align: center;
line-height: 60px;
}
</style>

View File

@ -0,0 +1,112 @@
<template>
<div>
<div v-if="hasData">
<a-row :gutter="12">
<a-col :span="12">
<ve-histogram :data="chartData" :settings="chartSetting[0]" :extend="chartExtend"></ve-histogram>
</a-col>
<a-col :span="12">
<ve-histogram :data="chartData" :settings="chartSetting[1]" :extend="chartExtend"></ve-histogram>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :span="12">
<ve-histogram :data="chartData" :settings="chartSetting[2]" :extend="chartExtend"></ve-histogram>
</a-col>
<a-col :span="12">
<ve-histogram :data="chartData" :settings="chartSetting[3]" :extend="chartExtend"></ve-histogram>
</a-col>
</a-row>
</div>
<div v-else style="text-align: center;margin-top: 100px">
无数据
</div>
</div>
</template>
<script>
// const echarts = require('echarts');
// import Model from './Model'
export default {
// name: "BasicChart",
data() {
return {
chartData: [],
chartSetting: [],
chartExtend:{
// xAxis:{ type: 'time' }
}
};
},
props: {
dataRow: { type: Array, default: () => [] },
labelMap: { type: Object, default: () => {} }
},
watch: {
dataRow() {
this.init()
}
},
computed: {
hasData() {
if(!!this.dataRow && this.dataRow.length > 0){
return true
}
return false
}
},
methods: {
setData() {
if (this.dataRow.length === 0) {
return;
}
this.dataRow.forEach(item => {
item.time = new Date(item.time).toLocaleString();
});
const cols = Object.keys(this.dataRow[0]);
this.chartData = {
columns: cols,
rows: this.dataRow
};
},
setSeting() {
this.chartSetting = [];
for (let key in this.labelMap) {
this.chartSetting.push({
labelMap: this.labelMap,
metrics: [key],
dimension: ["time"]
});
}
},
init() {
this.setData();
this.setSeting();
}
},
async mounted() {
this.init();
}
};
</script>
<style>
#setting {
display: none;
}
#chart {
width: 100%;
height: 100%;
}
</style>

View File

@ -3,10 +3,11 @@ import App from './App.vue'
import store from './utils/store'
import router from './utils/router'
import Antd from 'ant-design-vue';
import VCharts from 'v-charts'
import 'ant-design-vue/dist/antd.css';
Vue.use(Antd)
Vue.use(VCharts)
Vue.config.productionTip = false
Vue.prototype.$store = store

View File

@ -14,6 +14,9 @@ export const URL_MAP = {
PROJECT_DETAIL: 'project/detail/',
SENSOR_LIST: 'sensor/',
SENSOR_DETAIL: 'sensor/detail/',
WATER_LIST: 'v1/api/water_detail/',
USER_LIST: 'user/',
USER_DETAIL: 'user/detail/'
}
@ -45,7 +48,46 @@ export async function logout()
}
export async function getData(url, json) {
let response = await instance.get(url, json)
let response = await instance.get(url, {
params: json
})
let data = await response.data
return data
}
export async function postJSON(url, json) {
const headerJSON = {
"Content-Type": "application/json"
};
let response = await instance.post(url, JSON.stringify(json), { headers: headerJSON })
let data = await response.data
return data
}
export async function putJSON(url, json) {
const headerJSON = {
"Content-Type": "application/json"
};
let response = await instance.put(url, JSON.stringify(json), { headers: headerJSON })
let data = await response.data
return data
}
export async function patchJSON(url, json) {
const headerJSON = {
"Content-Type": "application/json"
};
let response = await instance.patch(url, JSON.stringify(json), { headers: headerJSON })
let data = await response.data
return data
}
export async function deleteJSON(url, json) {
const headerJSON = {
"Content-Type": "application/json"
};
let response = await instance.delete(url, { data:json })
let data = await response.data
return data
}

View File

@ -1,15 +1,14 @@
import { getData } from './http'
function processDevice(store)
{
const { deviceList, projectList, sensorList } = store.state
let deviceIdName = {}
let projectIdName = {}
let sensorIdName = {}
deviceList.forEach(d => deviceIdName[d._id] = d.device_name)
projectList.forEach(p => projectIdName[p._id.$oid] = p.name)
sensorList.forEach(s => sensorIdName[s._id.$oid] = s.name)
export function mergeList(a1=[], a2=[], key='_id') {
let a = []
let u = []
a1.forEach(uu => {
a.push(uu)
u.push(uu[key])
})
a2.forEach(uu => {
if(!u.includes(uu[key])) {
a.push(uu)
}
})
return a
}

27
src/utils/water.js Normal file
View File

@ -0,0 +1,27 @@
export function processWater(water, label){
const waters = []
water.forEach( w => {
let n = {}
label.forEach( key => {
if(!!w.data) {
n[key] = w.data[key]
}
} )
n['time'] = new Date(w.data.collect_time)
waters.push(n)
} )
return waters
}
export function processTimeData(dataArr=[], start=new Date(), end=new Date(), step=1) {
const data_re = []
for(let i=start.getTime(); i<=end.getTime(); i+=step)
{
data_re.push({
})
}
}