提交: 項目管理 詳情頁 设备管理和 项目信息 代码

This commit is contained in:
23688nl 2022-08-26 18:05:59 +08:00
parent 11d8226408
commit 3e91c55148
34 changed files with 4830 additions and 9 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -11,7 +11,7 @@
<link rel="stylesheet" href="//at.alicdn.com/t/font_2506643_9w119og75cs.css"> <link rel="stylesheet" href="//at.alicdn.com/t/font_2506643_9w119og75cs.css">
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_2506643_9xrjupanl8d.css"> <link rel="stylesheet" href="//at.alicdn.com/t/c/font_2506643_d5as4r89keo.css">
<script type="text/javascript" src="http://webapi.amap.com/maps?v=1.4.15&key=aabc97a5e095102787d405719847ecf0"></script> <script type="text/javascript" src="http://webapi.amap.com/maps?v=1.4.15&key=aabc97a5e095102787d405719847ecf0"></script>
<script src="<%= BASE_URL %>js/config.js"></script> <script src="<%= BASE_URL %>js/config.js"></script>

View File

@ -0,0 +1,28 @@
import request from "@/utils/request";
// 查询 项目 网关/子设备
export function listProjectDevice(query) {
return request({
url: "/iot/device/getProjectDeviceList",
method: "get",
params: query
});
}
// 查询 项目 型号
export function listProjectModel(query) {
return request({
url: "/iot/model/getProjectModelList",
method: "get",
params: query
});
}
// 查询 项目 子设备 表格 列
export function listProjectTableFilter(query) {
return request({
url: "/iot/dev/prod/fun/tableHeader",
method: "get",
params: query
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,8 @@
import ESimpleCard from "./src/ESimpleCard/index"
ESimpleCard.install = function install(Vue) {
Vue.component(ESimpleCard.name, ESimpleCard);
};
export default ESimpleCard

View File

@ -0,0 +1,84 @@
import './style.scss'
export default {
name: 'ESimpleCard',
props: {
direction: {
type: String,
default: 'horizontal'
},
title: {
type: String,
default: ''
},
extra: {
type: [String, Boolean],
default: false
},
labelStyle: {
type: Object
},
contentStyle: {
type: Object
},
labelClassName: {
type: String,
default: ''
},
contentClassName: {
type: String,
default: ''
},
data: {
type: [String, Object, Number, Array]
},
colon: {
type: Boolean,
default: true
},
icon: {
type: String,
default: 'el-icon-s-grid'
}
},
methods: {
handleExtra() {
const { data } = this;
this.$emit('click', data)
},
},
render() {
const { title, extra, $slots, icon, color } = this;
return (
<div class="e-simple-card">
{
(title || extra || $slots.cardHeader) ?
<div class="e-simple-card__header">
{ $slots.cardHeader ||
<template class="e-header-template">
<div class="template-left">
<i class={[ icon ? icon : 'el-icon-s-grid', 'template-icon' ]}></i>
<div class="template-title">
{ title }
</div>
</div>
{ extra ?
<div class="template__extra">
<i class="el-icon-setting icon" onClick={this.handleExtra}></i>
</div> : null
}
</template>
}
</div>
: null
}
<div class="e-simple-card__body">
{
$slots.cardBody
}
</div>
</div>
)
}
}

View File

@ -0,0 +1,53 @@
.e-simple-card {
width: 100%;
height: 100%;
background: #FFFFFF;
border: 1px solid #E8E8E8;
border-radius: 5px;
}
.e-simple-card__header {
border-bottom: 1px solid #E8E8E8;
padding: 10px;
}
.e-simple-card__body {
padding: 20px;
}
.e-simple-card__header .e-header-template {
display: flex;
width: 100%;
height: 15px;
align-items: center;
}
.e-header-template .template-left {
width: 60%;
height: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
}
.e-header-template .template-left .template-title {
font-size: 14px;
font-family: 'Source Han Sans CN';
font-weight: 400;
color: #344567;
}
.e-header-template .template-left .template-icon {
font-size: 14px;
margin-right: 5px;
color: #344567;
}
.e-header-template .template__extra {
width: 40%;
height: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
font-size: 16px;
}
.e-header-template .template__extra .icon:hover {
color: #1890ff
}

View File

@ -0,0 +1,9 @@
import Descriptions from './src/index';
import './style.scss'
/* istanbul ignore next */
Descriptions.install = function install(Vue) {
Vue.component(Descriptions.name, Descriptions);
};
export default Descriptions;

View File

@ -0,0 +1,30 @@
export default {
name: 'ElDescriptionsItem',
props: {
label: {
type: String,
default: ''
},
span: {
type: Number,
default: 1
},
contentClassName: {
type: String,
default: ''
},
contentStyle: {
type: Object
},
labelClassName: {
type: String,
default: ''
},
labelStyle: {
type: Object
}
},
render() {
return null;
}
};

View File

@ -0,0 +1,116 @@
export default {
name: 'ElDescriptionsRow',
props: {
row: {
type: Array
}
},
inject: ['elDescriptions'],
render(h) {
const { elDescriptions } = this;
const row = (this.row || []).map(item => {
return {
...item,
label: item.slots.label || item.props.label,
...['labelClassName', 'contentClassName', 'labelStyle', 'contentStyle'].reduce((res, key) => {
res[key] = item.props[key] || elDescriptions[key];
return res;
}, {})
};
});
if (elDescriptions.direction === 'vertical') {
return (
<tbody>
<tr class="el-descriptions-row">
{
row.map(item => {
return (
<th
class={{
'el-descriptions-item__cell': true,
'el-descriptions-item__label': true,
'has-colon': elDescriptions.border ? false : elDescriptions.colon,
'is-bordered-label': elDescriptions.border,
[item.labelClassName]: true
}}
style={item.labelStyle}
colSpan={item.props.span}
>{item.label}</th>
);
})
}
</tr>
<tr class="el-descriptions-row">
{
row.map(item =>{
return (
<td
class={['el-descriptions-item__cell', 'el-descriptions-item__content', item.contentClassName]}
style={item.contentStyle}
colSpan={item.props.span}
>{item.slots.default}</td>
);
})
}
</tr>
</tbody>
);
}
if (elDescriptions.border) {
return (
<tbody>
<tr class="el-descriptions-row">
{
row.map(item=> {
return ([
<th
class={{
'el-descriptions-item__cell': true,
'el-descriptions-item__label': true,
'is-bordered-label': elDescriptions.border,
[item.labelClassName]: true
}}
style={item.labelStyle}
colSpan="1"
>{item.label}</th>,
<td
class={['el-descriptions-item__cell', 'el-descriptions-item__content', item.contentClassName]}
style={item.contentStyle}
colSpan={item.props.span * 2 - 1}
>{item.slots.default}</td>
]);
})
}
</tr>
</tbody>
);
}
return (
<tbody>
<tr class="el-descriptions-row">
{
row.map(item=> {
return (
<td class="el-descriptions-item el-descriptions-item__cell" colSpan={item.props.span}>
<div class="el-descriptions-item__container">
<span
class={{
'el-descriptions-item__label': true,
'has-colon': elDescriptions.colon,
[item.labelClassName]: true
}}
style={item.labelStyle}
>{item.label}</span>
<span
class={['el-descriptions-item__content', item.contentClassName]}
style={item.contentStyle}
>{item.slots.default}</span>
</div>
</td>);
})
}
</tr>
</tbody>
);
}
};

View File

@ -0,0 +1,180 @@
import DescriptionsRow from './descriptions-row';
import { isFunction } from 'element-ui/src/utils/types';
export default {
name: 'ElDescriptions',
components: {
[DescriptionsRow.name]: DescriptionsRow
},
props: {
border: {
type: Boolean,
default: false
},
column: {
type: Number,
default: 3
},
direction: {
type: String,
default: 'horizontal'
},
size: {
type: String
// validator: isValidComponentSize,
},
title: {
type: String,
default: ''
},
extra: {
type: String,
default: ''
},
labelStyle: {
type: Object
},
contentStyle: {
type: Object
},
labelClassName: {
type: String,
default: ''
},
contentClassName: {
type: String,
default: ''
},
colon: {
type: Boolean,
default: true
}
},
computed: {
descriptionsSize() {
return this.size || (this.$ELEMENT || {}).size;
}
},
provide() {
return {
elDescriptions: this
};
},
methods: {
getOptionProps(vnode) {
if (vnode.componentOptions) {
const componentOptions = vnode.componentOptions;
const { propsData = {}, Ctor = {} } = componentOptions;
const props = (Ctor.options || {}).props || {};
const res = {};
for (const k in props) {
const v = props[k];
const defaultValue = v.default;
if (defaultValue !== undefined) {
res[k] = isFunction(defaultValue) ? defaultValue.call(vnode) : defaultValue;
}
}
return { ...res, ...propsData };
}
return {};
},
getSlots(vnode) {
let componentOptions = vnode.componentOptions || {};
const children = vnode.children || componentOptions.children || [];
const slots = {};
children.forEach(child => {
if (!this.isEmptyElement(child)) {
const name = (child.data && child.data.slot) || 'default';
slots[name] = slots[name] || [];
if (child.tag === 'template') {
slots[name].push(child.children);
} else {
slots[name].push(child);
}
}
});
return { ...slots };
},
isEmptyElement(c) {
return !(c.tag || (c.text && c.text.trim() !== ''));
},
filledNode(node, span, count, isLast = false) {
if (!node.props) {
node.props = {};
}
if (span > count) {
node.props.span = count;
}
if (isLast) {
// set the max span, cause of the last td
node.props.span = count;
}
return node;
},
getRows() {
const children = ((this.$slots.default || []).filter(vnode => vnode.tag &&
vnode.componentOptions && vnode.componentOptions.Ctor.options.name === 'ElDescriptionsItem'));
const nodes = children.map(vnode => {
return {
props: this.getOptionProps(vnode),
slots: this.getSlots(vnode),
vnode
};
});
const rows = [];
let temp = [];
let count = this.column;
nodes.forEach((node, index) => {
const span = node.props.span || 1;
if (index === children.length - 1) {
temp.push(this.filledNode(node, span, count, true));
rows.push(temp);
return;
}
if (span < count) {
count -= span;
temp.push(node);
} else {
temp.push(this.filledNode(node, span, count));
rows.push(temp);
count = this.column;
temp = [];
}
});
return rows;
}
},
render() {
const { title, extra, border, descriptionsSize, $slots } = this;
const rows = this.getRows();
return (
<div class="el-descriptions">
{
(title || extra || $slots.title || $slots.extra)
? <div class="el-descriptions__header">
<div class="el-descriptions__title">
{ $slots.title ? $slots.title : title}
</div>
<div class="el-descriptions__extra">
{ $slots.extra ? $slots.extra : extra }
</div>
</div>
: null
}
<div class="el-descriptions__body">
<table class={['el-descriptions__table', {'is-bordered': border}, descriptionsSize ? `el-descriptions--${descriptionsSize}` : '']}>
{rows.map(row => (
<DescriptionsRow row={row}></DescriptionsRow>
))}
</table>
</div>
</div>
);
}
};

View File

@ -11,6 +11,7 @@ let wsProtocol = 'ws://';
let iotWebSocketAlarmBaseUrl = '' let iotWebSocketAlarmBaseUrl = ''
let bigWebSocketUrl = '' let bigWebSocketUrl = ''
let port = '8899' let port = '8899'
let webSocketProjectGatewayUrl = ''
// window.dasConfig = { // window.dasConfig = {
// ip: 'http://192.168.10.241:32024' // ip: 'http://192.168.10.241:32024'
@ -41,8 +42,9 @@ if (env.NODE_ENV == 'development') {
} else if(env.NODE_ENV == 'test') { } else if(env.NODE_ENV == 'test') {
} }
iotWebSocketAlarmBaseUrl = sysWebSocket + hrefHost + ':8899/ws/alarm/live' iotWebSocketAlarmBaseUrl = sysWebSocket + hrefHost + ':8899/ws/alarm/live'
bigWebSocketUrl = sysWebSocket + hrefHost + ( port ? ':'+ port :'') + '/ws/dev/readData' sysWebSocket = sysWebSocket + hrefHost + ( port ? ':'+ port :'');
sysWebSocket = sysWebSocket + hrefHost; bigWebSocketUrl = sysWebSocket + '/ws/dev/readData'
webSocketProjectGatewayUrl = sysWebSocket + '/ws/dev/projectDeviceLive'
iotWebSocketBaseUrl = sysWebSocket + '/ws/dev/up/' iotWebSocketBaseUrl = sysWebSocket + '/ws/dev/up/'
devLiveWebSocketBaseUrl = sysWebSocket + '/ws/dev/live/' devLiveWebSocketBaseUrl = sysWebSocket + '/ws/dev/live/'
iotPlatformUrl = 'http://' + iotHost + prodApi iotPlatformUrl = 'http://' + iotHost + prodApi
@ -54,5 +56,6 @@ export {
iotWebSocketAlarmBaseUrl, iotWebSocketAlarmBaseUrl,
devLiveWebSocketBaseUrl, devLiveWebSocketBaseUrl,
prodApi, prodApi,
bigWebSocketUrl bigWebSocketUrl,
webSocketProjectGatewayUrl
} }

View File

@ -274,9 +274,9 @@
</div> </div>
</el-dialog> </el-dialog>
<div class="to-home-wrap2" @click="linkToTable" v-show="componectVal !== ''"> <!-- <div class="to-home-wrap2" @click="linkToTable" v-show="componectVal !== ''">
<el-button icon="el-icon-d-arrow-left" title="返回列表" circle>返回列表</el-button> <el-button icon="el-icon-d-arrow-left" title="返回列表" circle>返回列表</el-button>
</div> </div> -->
</div> </div>
</template> </template>
@ -647,7 +647,7 @@ export default {
}, },
handleDetails(row) { handleDetails(row) {
this.sourceId = row.projectId; this.sourceId = row.projectId;
this.componectVal = "DetailsWrap"; this.componectVal = "DetailsWrapNew";
}, },
// //
linkToTable() { linkToTable() {

View File

@ -0,0 +1,442 @@
<template>
<div class="e-object-device-children-table">
<div class="device-children-header">
<div class="left-link-home" @click="handleLinkToHome">
<icon class="iconfont iconfanhui_1"></icon>
<span class="link-bt-title">返回</span>
</div>
<div class="lift-gateway-info">
<span class="gateway-label">位置</span>
<span class="gateway-adders">{{
gatewayDevice.deviceAddress || "--"
}}</span>
<span class="gateway-label">设备号</span>
<span class="gateway-did">{{ gatewayDevice.deviceId || "--" }}</span>
<span class="gateway-label">在线状态</span>
<span
:class="
gatewayDevice.deviceState === 'ONLINE'
? 'gateway-stu'
: 'gateway-stu off-line'
"
>
{{ gatewayDevice.deviceState === "ONLINE" ? "在线" : "离线" }}
</span>
</div>
<div class="right-operate">
<div class="operate-refresh" @click="handleRefresh">
刷新
</div>
<div :class="tableSelectList && tableSelectList.length > 0 ? 'operate-onekey-on' : 'operate-onekey-on but-disable'" @click="handleChildStatus('true')">
一键合闸
</div>
<div :class="tableSelectList && tableSelectList.length > 0 ? 'operate-onekey-on' : 'operate-onekey-on but-disable'" @click="handleChildStatus('false')">
一键分闸
</div>
<el-select v-model="queryParams.prodKey" style="margin-left: 5xp; width: 120px;" placeholder="请选择项目类型" size="small">
<el-option
v-for="dict in projectModelList"
:key="dict.prodKey"
:label="dict.modelName"
:value="dict.prodKey"
@change="getTableFilterList"
></el-option>
</el-select>
</div>
</div>
<div class="device-children-center">
<e-dynamic-table
:border="false"
:loading="tableLoading"
:tableTotal="0"
:queryParams="queryParams"
:tableList="tableList"
:filterList="[...defaultFilterList, ...filterList]"
:isIndex="true"
:isSelect="true"
rowKey="deviceId"
@tableSelectionChange="handleTableChange"
@handleQuery="handleQuery"
>
<template v-slot:operate="operate">
<div>
<span>{{ operate }}</span>
</div>
</template>
</e-dynamic-table>
</div>
</div>
</template>
<script>
import EDynamicTable from "./EDynamicTable";
import { listProjectDevice, listProjectModel, listProjectTableFilter } from '@/api/iot/project_new'
import { setSwitchControl } from "@/api/iot/device";
import { webSocketProjectGatewayUrl } from "@/config/env";
export default {
name: "EDeviceChildren",
components: {
EDynamicTable,
},
props: {
gatewayDevice: {
type: Object,
require: true,
},
sourceId: {
type: [String, Number],
require: true,
},
deviceType: {
type: String,
default: "",
},
},
data() {
return {
queryParams: {
prodKey: undefined,
pageNum: 1,
pageSize: 10,
},
tableLoading: true,
tableList: [],
defaultFilterList: [
{
label: "线路名称",
align: "center",
prop: "deviceName",
width: "150px"
},
{
label: "型号",
align: "center",
prop: "modelName",
width: "150px",
},
],
filterList: [],
operate: {
label: "操作",
align: "left",
prop: "operate",
width: "200px",
slot: true,
slotName: "operate",
},
projectModelList: [],
tableSelectList: [],
stompClient: null,
socket_flag: true,
timeout_flag: null
};
},
created() {
this.tableLoading = true
this.getProjectModelList()
},
methods: {
handleLinkToHome() {
this.$emit("handleLinkToHome");
},
//
handleTableFilter(list) {
var resultList = [];
if (list && list.length > 0) {
list.forEach(v => {
resultList.push({
label: v['unitName'] ? `${v['name']}(${v['unitName']})` : v['name'],
align: "center",
prop: v['key'],
width: list.length > 5 ? '200px' : '',
})
});
};
return resultList;
},
//
getProjectModelList() {
listProjectModel({
projectId: this.sourceId,
deviceTags: this.deviceType || '',
pid: this.gatewayDevice.deviceId || ''
}).then(res => {
this.projectModelList = res.data
if (this.projectModelList && this.projectModelList.length > 0) {
this.queryParams.prodKey = this.projectModelList[0]['prodKey']
this.getTableFilterList()
};
})
},
//
getTableFilterList() {
this.filterList = []
this.getChildrenDeviceList()
listProjectTableFilter({
prodKey: this.queryParams.prodKey
}).then(res => {
this.filterList = this.handleTableFilter(res.data)
this.$forceUpdate()
this.tableLoading = false
})
},
// ---
getChildrenDeviceList() {
this.tableList = []
listProjectDevice(Object.assign({
projectId: this.sourceId || '',
deviceTags: this.deviceType || '',
pid: this.gatewayDevice.deviceId || ''
}, this.queryParams)).then(res => {
this.tableList = res.data
if (this.stompClient) {
this.closeSocket()
}
this.connection()
})
},
//
handleTableChange(e) {
if (e && e.length > 0) {
this.tableSelectList = e.map(v => v['deviceId'])
} else {
this.tableSelectList = []
}
},
// 线
handleChildStatus(type) {
this.$prompt("请输入登录密码", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
inputPattern: /^[a-z A-z 0-9 $.]+/,
inputType: "password",
inputErrorMessage: "登录密码不能为空",
}).then(({ value }) => {
let params = {
data: {
cmd: "set_switch",
params: {
switch: 1
},
},
deviceId: '',
verifyKey: value,
};
switch(type) {
case 'true':
params.data.params.switch = 1;
params.deviceId = this.tableSelectList.toString();
break;
case 'false':
params.data.params.switch = 0;
params.deviceId = this.tableSelectList.toString();
break;
}
setSwitchControl(params).then((res) => {
this.msgSuccess("修改成功");
});
});
},
handleRefresh() {
this.queryParams.pageNum = 1;
this.getChildrenDeviceList()
},
//
handleQuery(e) {
this.queryParams = Object.assign(this.queryParams, e)
this.getChildrenDeviceList()
},
// socket
handleDeviceInfo(param) {
if (this.tableList && this.tableList.length > 0) {
this.tableList = this.tableList.map(v => {
if (v['deviceId'] === param['deviceId']) {
return Object.assign(v, param)
} else {
return v
}
})
}
},
// ws
connection() {
if (this.stompClient) {
return;
}
if (!webSocketProjectGatewayUrl) {
return;
}
if (!this.tableList || this.length <= 0) {
return;
}
let deviceIds = this.tableList.map(v => v['deviceId'])
this.stompClient = new WebSocket(
`${webSocketProjectGatewayUrl}/${this.getGuid()}/${ deviceIds.toString() }`
);
this.stompClient.onmessage = this.socket_message;
this.stompClient.onclose = this.socket_onclose;
},
socket_message(evt) {
console.log("wsljcg:=", evt);
const data = JSON.parse(evt.data);
// this.actualEnergyCensus = data.params || {};
console.log('socket-message:', data)
this.handleDeviceInfo(data)
this.$forceUpdate();
},
socket_onclose(e) {
this.stompClient = null;
if (this.socket_flag) {
this.socket_flag = false;
let _this = this;
this.timeout_flag = setTimeout(function () {
_this.socket_flag = true;
_this.connection();
}, 10000);
}
},
closeSocket() {
if (this.stompClient) {
this.stompClient.close();
}
this.socket_flag = false;
this.stompClient = null;
clearTimeout(this.timeout_flag);
},
getGuid() {
return "xxxxxxx_xxxxx_4xxx_yxxx_xxxxxxxxxxxx".replace(/[xy]/g, function(
c
) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
},
},
destroyed() {
this.closeSocket()
}
};
</script>
<style lang="scss">
.e-object-device-children-table {
width: 100%;
height: 100%;
.device-children-header {
width: 100%;
height: 50px;
display: flex;
align-items: center;
.left-link-home {
width: 60px;
height: 30px;
background: #f4f5f7;
border-radius: 5px;
font-size: 12px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #6b778c;
display: flex;
align-items: center;
justify-content: center;
.link-bt-title {
margin-left: 5px;
}
}
.lift-gateway-info {
width: calc(100% - 420px);
height: 100%;
display: flex;
align-items: center;
padding-left: 10px;
justify-content: space-around;
.gateway-label {
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 300;
color: #6b778c;
text-overflow: ellipsis;
display: inline-block;
white-space: nowrap;
overflow: hidden;
}
.gateway-adders {
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 300;
color: #344567;
width: calc(45% - 60px);
text-overflow: ellipsis;
display: inline-block;
white-space: nowrap;
overflow: hidden;
}
.gateway-did {
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 300;
color: #344567;
width: calc(55% - 20% - 80px);
text-overflow: ellipsis;
display: inline-block;
white-space: nowrap;
overflow: hidden;
}
.gateway-stu {
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 300;
color: #009003;
width: calc(18% - 80px);
text-overflow: ellipsis;
display: inline-block;
white-space: nowrap;
overflow: hidden;
}
.off-line {
color: #ff0000;
}
}
.right-operate {
width: 350px;
display: flex;
justify-content: flex-end;
font-weight: 400;
color: #344567;
font-size: 12px;
font-family: Source Han Sans CN;
cursor: default;
.operate-refresh {
width: 60px;
height: 30px;
background: #F4F5F7;
border-radius: 5px;
display: flex;
justify-content: center;
align-items: center;
margin: 0 5px;
}
.operate-onekey-on {
width: 75px;
height: 30px;
background: #F4F5F7;
border-radius: 5px;
display: flex;
justify-content: center;
align-items: center;
margin: 0 5px;
font-weight: 400;
color: #344567;
}
.but-disable {
color: #6B778C;
opacity: 0.5;
}
}
}
}
</style>

View File

@ -0,0 +1,512 @@
<template>
<div class="e-object-device-manage-table">
<el-form
:model="queryParams"
ref="queryForm"
:inline="true"
label-width="68px"
v-show="showChildrenView === false"
>
<el-row>
<el-col :span="5">
<el-form-item label="型号名称" prop="modelName">
<!-- <el-input
v-model="queryParams.modelName"
placeholder="请输入型号名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/> -->
<el-select v-model="queryParams.prodKey" placeholder="请选择项目类型" clearable size="small">
<el-option
v-for="dict in projectModelList"
:key="dict.prodKey"
:label="dict.modelName"
:value="dict.prodKey"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="设备名称" prop="deviceName">
<el-input
v-model="queryParams.deviceName"
placeholder="请输入设备名称"
clearable
size="small"
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="设备状态" prop="deviceStatus">
<el-input
v-model="queryParams.deviceStatus"
placeholder="请输入设备状态"
clearable
size="small"
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="设备类型" prop="deviceStatus">
<el-input
v-model="queryParams.deviceStatus"
placeholder="请输入设备类型"
clearable
size="small"
/>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item class="query-foot">
<el-button
type="primary"
icon="el-icon-search"
size="mini"
@click="handleQuery"
>搜索</el-button
>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
>重置</el-button
>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="table-container" v-show="showChildrenView === false">
<e-simple-card title="" extra="" class="object-device-card" v-for="(devItem, idx) in list" :key="idx">
<template slot="cardHeader" class="card-custom-head">
<div :class="devItem['deviceState'] == 'ONLINE' ? 'card-left' : 'card-left off-line'">
<icon v-if="devItem['deviceState'] == 'ONLINE'" class="iconfont iconSYS_STA_1"/>
<icon v-else class="iconfont iconlixian"/>
<span style="margin-left: 8px;">{{ devItem['deviceState'] == 'ONLINE' ? '在线' : '离线' }}</span>
</div>
<div class="card-right">
<span class="c-label">线路数:</span>
<span class="c-number">{{ devItem.routeCount || 0}}</span>
<el-dropdown>
<icon class="el-icon-s-operation card-opt-i"/>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="handleChildrenDevice(devItem)">查看线路列表</el-dropdown-item>
<el-dropdown-item>修改服务指向</el-dropdown-item>
<el-dropdown-item>设备巡检记录</el-dropdown-item>
<el-dropdown-item>设备参数调整</el-dropdown-item>
<el-dropdown-item>内置定时配置</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</template>
<template slot="cardBody">
<div class="object-device-body">
<div class="card-body-info">
<div class="card-body-left">
<img :src="devItem['deviceImage'] ? devItem['deviceImage'] : '/images/devcie_default.png'" />
</div>
<div class="card-body-right">
<span :title="devItem.deviceId">{{ devItem.deviceId }}</span>
<span :title="devItem.deviceSecret">{{ devItem.deviceSecret }}</span>
<span :title="devItem.deviceAddress">{{ devItem.deviceAddress }}</span>
</div>
</div>
<div class="card-body-footer">
<!-- {{ renderCardFooter(devItem) }} -->
<div :class="devItem.alarmProcessStatus == 1 ? 'card-footer' : 'card-footer dev-error'">
<icon class="iconfont icontongzhi"></icon>
<span stle="margin-left: 10px;" class="footer-title">{{ devItem.alarmProcessStatus == 1 ? '正常' : `时间:${devItem.alarmTime} ${devItem.alarmTypeName || '--'}` }}</span>
</div>
</div>
</div>
</template>
</e-simple-card>
</div>
<div v-if="showChildrenView">
<e-device-children
:gatewayDevice="showGatewayInfo"
:sourceId="sourceId"
:deviceType="deviceType"
@handleLinkToHome="handleLinkToHome"></e-device-children>
</div>
</div>
</template>
<script>
import ESimpleCard from "@/components/Cards/index";
import EDeviceChildren from './EDeviceChildren'
import { webSocketProjectGatewayUrl } from "@/config/env";
import { listProjectDevice, listProjectModel } from '@/api/iot/project_new'
export default {
name: "EDeviceTable",
components: {
ESimpleCard,
EDeviceChildren
},
props: {
sourceId: {
type: [String, Number],
require: true,
},
deviceType: {
type: String,
default: "",
},
},
data() {
return {
queryParams: {
prodKey: undefined,
deviceName: undefined,
deviceStatus: undefined,
},
showChildrenView: false,
list: [],
mockerList: [
{
createTime: "2022-07-27 16:33:58",
deviceAddress: "福建省福州市闽侯县甘蔗街道闽侯榕新工艺有限公司",
deviceId: "821d1121a4bf426883a5266f12f8b758",
deviceKey: "ceshixinzeng",
deviceName: "ceshixinzeng",
deviceSecret: "bc6eafae957b43f682d6eaef56aac988",
deviceState: "UNACTIVE",
deviceStatus: "0",
deviceType: "GATEWAY_CONTROLLER",
deviceTypeName: "物联网网关",
modelId: "03e27dec4cb6464c809906fa41ddf7dc",
modelName: "网关-测试型号0513",
prodKey: "testpk220513",
updateTime: "2022-07-27 16:33:59",
parentId: "0",
},
{
createTime: "2022-07-27 16:33:58",
deviceAddress: "福建省福州市闽侯县甘蔗街道闽侯榕新工艺有限公司",
deviceId: "821d1121a4bf426883a5266f12f8b758",
deviceKey: "ceshixinzeng",
deviceName: "ceshixinzeng",
deviceSecret: "bc6eafae957b43f682d6eaef56aac988",
deviceState: "UNACTIVE",
deviceStatus: "1",
deviceType: "GATEWAY_CONTROLLER",
deviceTypeName: "物联网网关",
modelId: "03e27dec4cb6464c809906fa41ddf7dc",
modelName: "网关-测试型号0513",
prodKey: "testpk220513",
updateTime: "2022-07-27 16:33:59",
parentId: "0",
},
{
createTime: "2022-07-27 16:33:58",
deviceAddress: "福建省福州市闽侯县甘蔗街道闽侯榕新工艺有限公司",
deviceId: "821d1121a4bf426883a5266f12f8b758",
deviceKey: "ceshixinzeng",
deviceName: "ceshixinzeng",
deviceSecret: "bc6eafae957b43f682d6eaef56aac988",
deviceState: "ONLINE",
deviceStatus: "0",
deviceType: "GATEWAY_CONTROLLER",
deviceTypeName: "物联网网关",
modelId: "03e27dec4cb6464c809906fa41ddf7dc",
modelName: "网关-测试型号0513",
prodKey: "testpk220513",
updateTime: "2022-07-27 16:33:59",
parentId: "0",
},
],
showGatewayInfo: {},
projectModelList: [],
stompClient: null,
socket_flag: true,
timeout_flag: null
};
},
watch: {
sourceId(val) {
if (val) {
this.initHTML();
}
},
deviceType(val) {
if (val) {
this.initHTML();
}
},
},
created() {
this.initHTML()
},
methods: {
// init html
initHTML() {
this.getGatewayList();
this.getProjectModelList();
},
//
handleChildrenDevice(gatewayDevice) {
this.showChildrenView = true;
this.showGatewayInfo = gatewayDevice;
this.$forceUpdate()
},
//
handleLinkToHome() {
this.showChildrenView = false;
},
//
renderCardContent(item) {
return (
<template>
<div class="card-body-left">
<image src={item['deviceImage'] ? item['deviceImage'] : '/images/devcie_default.png'}></image>
</div>
<div class="card-body-right">
<span>{ item.deviceId }</span>
<span>{ item.deviceSecret }</span>
<span>{ item.deviceAddress }</span>
</div>
</template>
)
},
//
renderCardFooter(item) {
return (
<template>
<div class={item.deviceStatus == 0 ? 'card-footer' : 'card-footer dev-error'}>
<icon class="iconfont icontongzhi"></icon>
<span>{item.deviceStatus == 0 ? '正常' : 'card-footer dev-error'}</span>
</div>
</template>
)
},
handleQuery() {
this.getGatewayList();
},
resetQuery() {
this.getGatewayList();
},
//
getProjectModelList() {
listProjectModel({
projectId: this.sourceId,
deviceTags: this.deviceType || ''
}).then(res => {
this.projectModelList = res.data
})
},
//
getGatewayList() {
this.list = []
listProjectDevice(Object.assign({
projectId: this.sourceId,
deviceTags: this.deviceType || ''
}, this.queryParams)).then(res => {
this.list = res.data
if (this.stompClient) {
this.closeSocket()
}
this.connection()
})
},
// ws
connection() {
if (this.stompClient) {
return;
}
if (!webSocketProjectGatewayUrl) {
return;
}
if (!this.list || this.length <= 0) {
return;
}
let deviceIds = this.list.map(v => v['deviceId'])
this.stompClient = new WebSocket(
`${webSocketProjectGatewayUrl}/${this.getGuid()}/${ deviceIds.toString() }`
);
this.stompClient.onmessage = this.socket_message;
this.stompClient.onclose = this.socket_onclose;
},
socket_message(evt) {
console.log("wsljcg:=", evt);
const data = JSON.parse(evt.data);
// this.actualEnergyCensus = data.params || {};
console.log('socket-message:', data)
this.handleDeviceInfo(data)
this.$forceUpdate();
},
// socket
handleDeviceInfo(param) {
if (this.list && this.list.length > 0) {
this.list = this.list.map(v => {
if (v['deviceId'] === param['deviceId']) {
return Object.assign(v, param)
} else {
return v
}
})
}
},
socket_onclose(e) {
this.stompClient = null;
if (this.socket_flag) {
this.socket_flag = false;
let _this = this;
this.timeout_flag = setTimeout(function () {
_this.socket_flag = true;
_this.connection();
}, 10000);
}
},
closeSocket() {
if (this.stompClient) {
this.stompClient.close();
}
this.socket_flag = false;
this.stompClient = null;
clearTimeout(this.timeout_flag);
},
getGuid() {
return "xxxxxxx_xxxxx_4xxx_yxxx_xxxxxxxxxxxx".replace(/[xy]/g, function(
c
) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
},
},
destroyed() {
this.closeSocket()
}
};
</script>
<style lang="scss">
.e-object-device-manage-table {
.el-form-item--medium .el-form-item__content {
width: calc(100% - 70px);
}
.query-foot.el-form-item--medium .el-form-item__content {
width: 100%;
}
.table-container {
width: 100%;
height: 100%;
overflow: auto;
display: flex;
flex-wrap: wrap;
justify-content: start;
.object-device-card {
width: 285px;
height: 164px;
background: #FFFFFF;
border: 1px solid #E4EBF4;
box-shadow: 0px 0px 8px 0px rgba(17,76,157,0.26);
border-radius: 5px;
margin: 5px;
.card-left {
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #00C805;
}
.off-line {
color: #DA2710;
}
.card-right {
display: flex;
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #6B778C;
align-items: baseline;
.c-label {}
.c-number {
color: #344567;
margin: 0 8px;
}
.card-opt-i {
font-size: 18px;
-ms-transform:rotate(90deg); /* IE 9 */
-webkit-transform:rotate(90deg); /* Safari and Chrome */
transform:rotate(90deg);
}
.card-opt-i:hover {
color: #46a6ff;
}
}
.e-simple-card__header {
width: 100%;
display: flex;
justify-content: space-between;
}
.e-simple-card__body {
padding: 10px;
}
.object-device-body {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
.card-body-info {
width: 100%;
height: 85px;
display: flex;
.card-body-left {
width: 40%;
display: flex;
justify-content: center;
align-items: center;
> img {
width: 93px;
height: 52px;
}
}
.card-body-right {
width: 60%;
display: flex;
flex-wrap: wrap;
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 300;
color: #344567;
>span {
display: block;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.card-body-footer {
width: 100%;
display: flex;
.card-footer {
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #344567;
width: 100%;
display: flex;
align-items: center;
.footer-title {
width: 100%;
margin-left: 10px;
display: block;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
.dev-error {
color: #DC3520;
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,122 @@
<template>
<div class="e-dynamic-table">
<el-table v-loading="loading" :data="tableList" :row-key="rowKey" :border="border" @selection-change="handleSelectionChange">
<el-table-column type="selection" v-if="isSelect" width="50" align="center" />
<el-table-column
v-if="isIndex"
type="index"
label="序号"
align="center"
:index="
(val) => {
return (
val + 1 + (queryParams.pageNum - 1) * this.queryParams.pageSize
);
}
"
width="80px"
></el-table-column>
<el-table-column
:label="tableItem.label || ''"
:align="tableItem.align || 'left'"
:prop="tableItem.prop || ''"
:width="tableItem.width || ''"
v-for="(tableItem, idx) in filterList"
:key="idx"
>
<template slot-scope="scope">
<slot v-if="tableItem.slot" :name="tableItem.slotName" :row="scope.row" ></slot>
<span v-else class="lay-table-textarea" :title="scope.row[tableItem.prop || '']">
{{ scope.row[tableItem.prop || ''] }}
</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableTotal > 0"
:total="tableTotal"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="handleQuery2"
/>
</div>
</template>
<script>
export default {
name: "EDynamicTable",
props: {
border: {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: false,
},
tableTotal: {
type: Number,
default: 0,
},
queryParams: {
type: Object,
default: () => {
return {
pageNum: 1,
pageSize: 10,
};
},
},
tableList: {
type: Array,
default: () => {
return [];
},
},
filterList: {
type: Array,
default: () => {
return [];
},
},
isIndex: {
type: Boolean,
default: false,
},
isSelect: {
type: Boolean,
default: false
},
rowKey: {
type: String,
default: 'id'
}
},
data() {
return {
filter: [
{
label: "",
align: 'left',
prop: '',
width: '',
slot: true,
slotName: 'inst'
},
],
};
},
methods: {
handleQuery2() {
this.$emit('handleQuery', this.queryParams)
},
//
handleSelectionChange(selection) {
this.$emit('tableSelectionChange', selection)
},
}
};
</script>

View File

@ -0,0 +1,48 @@
<template>
<div class="e-object-device-manage">
<el-tabs v-model="activeName">
<el-tab-pane :label="tabLabel.dictLabel" :name="tabLabel.dictValue" v-for="(tabLabel, idx) in projectDeviceTagGroup" :key="idx">
<e-device-table v-if="tabLabel.dictValue === activeName" :sourceId="projectInfo['projectId']" :deviceType="tabLabel.dictValue" />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import EDeviceTable from './EDeviceTable'
export default {
name: 'EObjectDeviceManage',
components: {
EDeviceTable
},
props: {
projectInfo: {
type: Object,
require: true
}
},
data() {
return {
activeName: '',
projectDeviceTagGroup: []
}
},
created() {
this.getDicts("project_device_tag_group").then(response => {
this.projectDeviceTagGroup = response.data;
if (this.projectDeviceTagGroup && this.projectDeviceTagGroup.length > 0) {
this.activeName = this.projectDeviceTagGroup[0]['dictValue'] || ''
};
});
},
methods: {
//
statusFormat(row, column) {
return this.selectDictLabel(this.projectDeviceTagGroup, row.industry);
},
//
getGatewayDeviceList() {
},
}
}
</script>

View File

@ -0,0 +1,81 @@
<template>
<div class="e-census-cards">
<div
class="card-foreach"
v-for="(item, index) in propList"
:key="index"
:style="{
width: `calc(100% / ${propList.length})`,
}"
>
<div class="card-for-item">
<div class="card-item-title">{{ item.label }}</div>
<div class="card-item-value">{{ result[item.key] || 222 }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "ECensusCards",
props: {
propList: {
type: Array,
require: true,
},
result: {
type: Object,
default: () => {
return {};
},
},
},
data() {
return {
list: [],
};
},
};
</script>
<style scoped lang="scss">
.e-census-cards {
width: 100%;
height: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
.card-foreach {
width: 100%;
height: 100%;
display: flex;
.card-for-item {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
.card-item-title {
display: flex;
font-size: 14px;
font-family: "Source Han Sans CN";
font-weight: 400;
color: #6b778c;
width: 100%;
height: 15px;
justify-content: center;
}
.card-item-value {
font-size: 24px;
font-family: "Source Han Sans CN";
font-weight: 500;
color: #344567;
width: 100%;
justify-content: center;
display: flex;
margin-top: 15px;
}
}
}
}
</style>

View File

@ -0,0 +1,141 @@
<template>
<div class="bigscreen-echarts-gauge">
<div :id="eId" :style="styles"></div>
</div>
</template>
<script>
import * as echarts from 'echarts/core';
import { GaugeChart } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';
export default {
name: "echartsRadarWrap",
props: {
eId: {
type: String
},
styles: {
type: String
},
colorList: {
type: [Array, String],
default: ["#27d0ec"]
},
option: {
stype: Number,
default: 0
}
},
data() {
return {
chart: null
};
},
created() {
this.chart = null;
echarts.use([GaugeChart, CanvasRenderer]);
},
mounted() {
this.drawLine();
},
methods: {
drawLine() {
if (!this.chart) {
this.chart = echarts.init(document.getElementById(this.eId));
}
const options = {
series: [
{
type: "gauge",
color: this.colorList,
min: 0,
max: 100,
progress: {
show: true,
width: 3
},
axisLine: {
lineStyle: {
width: 3
}
},
axisTick: {
show: false
},
splitLine: {
distance: 2,
length: '3',
lineStyle: {
width: 1,
color: '#dcdcdc'
}
},
axisLabel: {
distance: 7,
color: '#999',
fontSize: 5
},
pointer: {
offsetCenter: [0, '0%'],
length: '60%',
width: '1',
itemStyle: {
// color: '#000'
}
},
anchor: {
show: true,
showAbove: true,
size: 6,
itemStyle: {
borderWidth: 1
}
},
title: {
show: false,
text: "今日报警"
},
axisLine: {
// 线
lineStyle: {
// lineStyle线
width: 4,
// color: [
// [1, "#1A3F81"]
// ]
}
},
detail: {
color: this.colorList[0] || "#27d0ec",
valueAnimation: true,
fontSize: 18,
offsetCenter: ['0', '80%']
},
data: [
{
value: this.option
}
]
}
]
};
this.chart.setOption(options);
}
},
watch: {
option(val, oldVal) {
this.chart = null;
this.drawLine();
}
}
};
</script>
<style lang="scss">
.bigscreen-echarts-gauge {
height: calc(100% - 30px);
height: calc(100% - 15px);
width: 100%;
display: flex;
justify-content: center;
}
</style>

View File

@ -0,0 +1,66 @@
<template>
<div class="project-e-trend-echart-line">
<div :id="eId" :style="styles"></div>
</div>
</template>
<script>
import * as echarts from "echarts";
export default {
name: "EEchartsLine",
props: {
eId: {
type: String
},
styles: {
type: String
},
colorList: {
type: [Array, String],
default: ""
},
option: {
stype: Object,
default: {}
}
},
data() {
return {
chart: null
};
},
created() {
this.chart = null;
},
mounted() {
this.drawLine();
},
methods: {
updateEchart() {
if (this.chart) {
this.chart = null;
}
this.drawLine();
},
drawLine() {
if (!this.chart) {
this.chart = echarts.init(document.getElementById(this.eId));
}
this.chart.setOption(this.option);
}
},
watch: {
option(val, oldVal) {
this.chart = null;
this.drawLine();
}
}
};
</script>
<style lang="scss">
.project-e-trend-echart-line {
position: relative;
height: 100%;
top: 0px;
width: 100%;
}
</style>

View File

@ -0,0 +1,122 @@
<template>
<div class="project-e-echarts-pie">
<div :id="eId" :style="styles"></div>
<!-- <div v-else>暂无数据</div> -->
</div>
</template>
<script>
import * as echarts from "echarts";
export default {
name: "echartsRadarWrap",
props: {
eId: {
type: String,
},
styles: {
type: String,
},
colorList: {
type: [Array, String],
default: "",
},
option: {
stype: Object,
default: [],
},
},
data() {
return {
chart: null,
};
},
created() {
this.chart = null;
},
mounted() {
this.drawLine();
},
methods: {
updateEchart() {
if (this.chart) {
this.chart = null;
}
this.drawLine();
},
drawLine() {
if (!this.chart) {
this.chart = echarts.init(document.getElementById(this.eId));
}
const option = {
color: this.colorList,
// legend: {
// top: "5",
// right: "10",
// type: "scroll",
// // itemWidth: 10, // legend
// orient: "vertical",
// pageIconColor: "#6495ed", //
// pageIconInactiveColor: "#aaa", //
// pageIconSize: 10, //
// pageButtonItemGap: 1, //
// textStyle: {
// color: "#fff",
// fontSize: 16,
// },
// formatter: function (name) {
// return name.length > 7 ? name.substr(0, 7) + "..." : name;
// },
// },
toolbox: {
show: false,
feature: {
mark: { show: true },
dataView: { show: true, readOnly: false },
restore: { show: true },
saveAsImage: { show: true },
},
},
series: [
{
name: "",
type: "pie",
radius: ["40%", "70%"],
center: ["50%", "50%"],
itemStyle: {
borderRadius: 10,
borderColor: "#fff",
borderWidth: 2,
},
emphasis: {
label: {
show: false,
},
},
label: {
show: false,
},
itemStyle: {
borderRadius: 1,
},
data: this.option,
},
],
};
this.chart.setOption(option);
},
},
watch: {
option(val, oldVal) {
this.chart = null;
this.drawLine();
},
},
};
</script>
<style lang="scss">
.project-e-echarts-pie {
position: relative;
height: 100%;
top: 0px;
width: 50%;
}
</style>

View File

@ -0,0 +1,91 @@
<template>
<div class="e-nav-menu">
<div
:class="value === item[props['key']] ? 'nav-menu-item menu-selected' : 'nav-menu-item'"
v-for="(item) in activeList"
:key="item[props['key']]"
@click="handleClick(item)"
>
<svg-icon slot="prefix" icon-class="A_yuanjiaodian" class="icon-left" />
<div class="nav-item-title">{{ item[props["label"]] }}</div>
<i class="el-icon-arrow-right icon-right"></i>
</div>
</div>
</template>
<script>
export default {
name: "ENavMenu",
props: {
value: {
type: [String, Number],
default: () => {
return 0
}
},
props: {
type: Object,
default: () => {
return {
label: "label",
key: "key",
};
},
},
activeList: {
type: Array,
default: () => {
return [
{
label: "项目管理",
key: "projectOt"
},
{
label: "设备管理",
key: "projectOt"
},
];
},
},
},
methods: {
handleClick(row) {
this.$emit('input', row[this.props['key']]);
}
}
};
</script>
<style scoped lang="scss">
.e-nav-menu {
width: 150px;
height: 100%;
padding-top: 15px;
border-right: 1px solid #E8E8E8;
.nav-menu-item {
width: 100%;
height: 32px;
background: #fff;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px;
.nav-item-title {
font-size: 14px;
font-family: 'Source Han Sans CN';
font-weight: 300;
color: #344567;
}
.icon-left {
font-size: 18px;
}
.icon-right {
font-size: 18px
}
}
.menu-selected {
background: #F4F5F7;
.nav-item-title {
color: #1890FF;
}
}
}
</style>

View File

@ -0,0 +1,47 @@
<template>
<div class="e-object-container">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="项目信息" name="info">
<e-object-info v-if="activeName === 'info'" :infoData="projectInfo || {}" :projectTypeOptions="projectTypeOptions"></e-object-info>
</el-tab-pane>
<el-tab-pane label="租户" name="tenant">
<e-object-tenant v-if="activeName === 'tenant'" :infoData="projectInfo || {}"/>
</el-tab-pane>
<el-tab-pane label="空间" name="space">
<e-object-space v-if="activeName === 'space'" :infoData="projectInfo || {}"/>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import EObjectInfo from './EObjectInfo'
import EObjectSpace from './EObjectSpace'
import EObjectTenant from './EObjectTenant'
export default {
name: 'EObjectContainer',
components: {
EObjectInfo,
EObjectSpace,
EObjectTenant
},
props: {
projectInfo: {
type: Object,
require: true
},
projectTypeOptions: {
type: Array
}
},
data() {
return {
activeName: 'info'
}
},
methods: {
handleClick(tab, event) {
console.log(tab, event);
}
}
}
</script>

View File

@ -0,0 +1,426 @@
<template>
<div class="e-object-info">
<div class="group-list-info">
<div class="top">
<div class="top-label">
<svg-icon
icon-class="A_product1"
style="margin-right: 2px; height: 20px; width: 20px"
/>
<span
v-if="updateState === false"
style="margin: 0px 15px 0 10px; font-weight: 200; font-size: 14px"
>项目名称{{ infoData.projectName }}</span
>
</div>
</div>
</div>
<div class="group-list-table">
<div class="table-row">
<div class="table-row-col">
<div class="title">项目ID</div>
<div class="content">
<span class="name">{{ infoData.projectId }}</span>
<el-button
type="text"
size="small"
@click.stop="copyText(infoData.projectId)"
>复制</el-button
>
</div>
</div>
<div class="table-row-col">
<div class="title">项目编号</div>
<div class="content">
<span class="group-id" :title="infoData.projectCode">{{
infoData.projectCode
}}</span>
</div>
</div>
<div class="table-row-col">
<div class="title">项目类型</div>
<div class="content">
<span class="group-id">{{ statusFormat(infoData) }}</span>
</div>
</div>
</div>
<div class="table-row">
<div class="table-row-col">
<div class="title">项目纬度</div>
<div class="content">{{ infoData.projectLat }}</div>
</div>
<div class="table-row-col">
<div class="title">项目经度</div>
<div class="content">{{ infoData.projectLng }}</div>
</div>
<div class="table-row-col">
<div class="title">项目状态</div>
<div class="content">
{{ infoData.projectStatus === "0" ? "启用" : "禁用" }}
</div>
</div>
</div>
<div class="table-row">
<div class="table-row-col">
<div class="title">总路设备</div>
<div class="content">{{ infoData.projectDeviceName || "--" }}</div>
</div>
<div class="table-row-col" style="flex: 2 0 0%">
<div class="title">项目地址</div>
<div class="content">
<span class="centent">{{ infoData.projectAddress }}</span>
</div>
</div>
</div>
<div class="table-row">
<div class="table-row-col">
<div class="title">创建时间</div>
<div class="content">{{ infoData.createTime || "--" }}</div>
</div>
<div class="table-row-col" style="flex: 2 0 0%">
<div class="title">行政区划</div>
<div class="content">{{ infoData.regionalismFullName }}</div>
</div>
</div>
</div>
<!-- ---- -->
<div class="group-list-info" style="margin-top: 20px">
<div class="top">
<div class="top-label">
<svg-icon
icon-class="A_product1"
style="margin-right: 2px; height: 20px; width: 20px"
/>
<span
v-if="updateState === false"
style="margin: 0px 15px 0 10px; font-weight: 200; font-size: 14px"
>{{ contrctInfo.contractName }}</span
>
</div>
</div>
</div>
<div class="group-list-table">
<div class="table-row">
<div class="table-row-col">
<div class="title">合同ID</div>
<div class="content">
<span class="name">{{ contrctInfo.contractId }}</span>
<el-button
type="text"
size="small"
@click.stop="copyText(contrctInfo.contractId)"
>复制</el-button
>
</div>
</div>
<div class="table-row-col">
<div class="title">合同类型</div>
<div class="content">
<span class="group-id">{{ contrctInfo.contractTypeName }}</span>
</div>
</div>
<div class="table-row-col">
<div class="title">合同状态</div>
<div class="content">
<span class="group-id">{{
contrctInfo.status === "0" ? "启用" : "禁用"
}}</span>
</div>
</div>
</div>
<div class="table-row">
<div class="table-row-col">
<div class="title" style="height: 160px">合同规则</div>
<div class="content" style="overflow: auto">
<div
class="crat-warp"
v-for="item in contrctInfo.priceContractRuleList"
:key="item.ruleNum"
>
<div class="title-span">{{ item.ruleName }}</div>
<div
class="time-wrap"
v-if="contrctInfo.contractType === 'PEAK_VALLEY'"
>
<span class="time-title">时间:</span>
<div class="time-val">
<span style="color: #3300ff">{{ item.minTime }}</span>
<span>~</span>
<span style="color: #3300ff">{{ item.maxTime }}</span>
</div>
</div>
<div class="time-wrap" v-else>
<div class="vlue-warp">
<span>最小值:</span>
<span>{{ item.minVal }}</span>
</div>
<div class="vlue-warp">
<span>最大值:</span>
<span>{{ item.maxVal }}</span>
</div>
</div>
<div class="dj-wrap">
<span class="dj-title">单价:</span>
<span class="dj-value">{{ item.unitPrice }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script >
import { getContract } from "@/api/iot/contract";
import ElDescriptions from "@/components/Edescriptions";
import ElDescriptionsItem from "@/components/Edescriptions/src/descriptions-item";
export default {
name: "EObjectInfo",
components: {
ElDescriptions,
ElDescriptionsItem,
},
props: {
infoData: {
type: Object,
require: true,
},
projectTypeOptions: {
type: Array,
},
},
watch: {
infoData(val) {
if (val) {
this.getContractById();
}
},
},
data() {
return {
updateState: false,
contrctInfo: {},
};
},
created() {
this.getContractById()
},
methods: {
//
statusFormat(row) {
return this.selectDictLabel(this.projectTypeOptions, row.industry);
},
copyText(val) {
this.copeFu(val, this);
},
getContractById() {
if (!this.infoData.contractId && this.infoData.contractId !== 0) {
return;
}
getContract(this.infoData.contractId).then((response) => {
this.contrctInfo = response.data;
});
},
},
};
</script>
<style lang="scss">
.e-object-info {
.group-list-info {
.top {
text-align: left;
width: 100%;
float: left;
height: 40px;
.top-label {
float: left;
width: calc(100% - 200px);
font-size: 16px;
font-weight: 700;
color: #373d41;
display: flex;
align-items: center;
justify-content: flex-start;
.el-input--medium {
width: 150px;
}
.el-button--text {
padding: 8px 12px;
color: #333;
}
.el-button--text:hover {
background-color: #d1dbe6;
border-radius: 0;
color: #1890ff;
}
}
.top-button {
float: left;
text-align: right;
width: 200px;
}
}
display: flex;
justify-content: space-between;
margin-bottom: 6px;
flex: 1;
.title {
line-height: 30px;
font-size: 16px;
font-weight: 700;
color: #373d41;
display: flex;
align-items: center;
flex: 1;
}
}
.group-list-table {
border-top: 1px solid #d4d4d4;
border-left: 1px solid #d4d4d4;
.table-row {
display: flex;
width: 100%;
.table-row-col {
display: flex;
flex: 1 1 0%;
overflow: hidden;
font-size: 14px;
.title {
width: 180px;
height: 48px;
color: rgb(116, 119, 122);
display: flex;
align-items: center;
background: rgb(250, 250, 252);
border-bottom: 1px solid #d4d4d4;
border-right: 1px solid #d4d4d4;
padding: 0px 12px;
margin-bottom: 0px;
font-size: 15px;
font-weight: 100;
}
.content {
display: flex;
justify-content: flex-start;
align-items: center;
flex: 1 1 0%;
overflow: hidden;
color: #666;
border-bottom: 1px solid #d4d4d4;
border-right: 1px solid #d4d4d4;
padding: 0px 12px;
.name {
display: inline-block;
max-width: 100%;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
overflow: hidden;
}
.group-id {
margin-right: 8px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
.notice-item {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 6px;
background: #d3d5d5;
margin-right: 3px;
}
.n {
background: #0fc18a;
}
.secret {
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
}
.crat-warp {
height: 100px;
width: 200px;
border: 1px solid #333;
padding: 5px;
border: 1px dotted #898989;
height: 120px;
width: 250px;
margin: 5px;
.title-span {
height: 30px;
font-size: 18px;
border-bottom: 1px dotted #333;
padding-left: 5px;
}
.time-wrap {
height: 50px;
display: flex;
width: 100%;
align-items: center;
font-size: 16px;
.time-title {
display: block;
width: 40px;
text-align: end;
margin-right: 5px;
}
.time-val {
display: flex;
font-size: 16px;
width: calc(100% - 45px);
justify-content: space-around;
}
.vlue-warp {
width: 50%;
display: flex;
justify-content: center;
> span:nth-child(1) {
display: block;
text-align: left;
margin-right: 5px;
width: 55px;
}
> span:nth-child(2) {
display: block;
width: calc(100% - 55px);
text-align: center;
color: #3300ff;
}
}
}
.dj-wrap {
height: 35px;
display: flex;
align-items: center;
font-size: 16px;
.dj-title {
display: block;
width: 40px;
text-align: end;
}
.dj-value {
display: block;
width: calc(100% - 45px);
text-align: center;
color: red;
}
}
}
}
</style>

View File

@ -0,0 +1,371 @@
<template>
<div class="app-container e-project-space">
<el-form
:model="queryParams"
ref="queryForm"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="上级空间" prop="parentId">
<treeselect
style="width: 200px;"
v-model="queryParams.parentId"
:options="querySpaceOptions"
:normalizer="normalizer"
placeholder="请选择上级空间"
/>
</el-form-item>
<el-form-item label="空间名称" prop="spaceName">
<el-input
v-model="queryParams.spaceName"
placeholder="空间名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="空间类型" prop="spaceType">
<el-select v-model="queryParams.spaceType" placeholder="空间类型" clearable>
<el-option
v-for="dict in spaceTypeOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="spaceList"
row-key="spaceId"
default-expand-all
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column type="index" label="序号" align="center" :index="indexFormatter" width="80px"></el-table-column>
<el-table-column label="空间名称" align="left" prop="spaceName" />
<!-- <el-table-column label="空间ID" align="left" prop="spaceId" />
<el-table-column label="空间编码" align="left" prop="spaceCode" />-->
<el-table-column label="空间类型" align="center" prop="spaceType" :formatter="statusFormat" />
<el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-search"
@click="handleDetails(scope.row)"
>空间设备</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加或修改项目空间对话框 -->
<el-dialog class="eldialog-wrap" :close-on-click-modal="false" :title="title" :visible.sync="open" width="500px">
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="上级空间:" prop="parentId">
<treeselect v-model="form.parentId" :options="spaceOptions" placeholder="请选择上级空间" />
</el-form-item>
<el-form-item label="空间名称:" prop="spaceName">
<el-input v-model="form.spaceName" placeholder="请输入空间名称" />
</el-form-item>
<el-form-item label="类型:" prop="spaceType">
<el-select v-model="form.spaceType" style="width: 100%;" placeholder="请选择空间类型">
<el-option
v-for="dict in spaceTypeOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
></el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button size="mini" type="primary" @click="submitForm"> </el-button>
<el-button size="mini" @click="cancel"> </el-button>
</div>
</el-dialog>
<el-dialog
class="eldialog-wrap"
:title="spaceDeviceTitle"
:visible.sync="selectTableShow"
width="75%"
top="10vh"
:close-on-click-modal="false"
>
<e-object-space-device
v-if="selectTableShow"
:sourceId="sourceId"
:projectId="infoData.projectId"
/>
<div slot="footer" class="dialog-footer">
<el-button size="mini" type="primary" @click="() =>{selectTableShow = false}"> </el-button>
<el-button size="mini" @click="() =>{selectTableShow = false}"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
listSpace,
getSpace,
delSpace,
addSpace,
updateSpace,
exportSpace,
listSpaceDevice,
listSpaceTree
} from "@/api/iot/space";
import EObjectSpaceDevice from "./EObjectSpaceDevice";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
name: "EObjectSpace",
components: {
Treeselect,
EObjectSpaceDevice
},
props: ["infoData"],
data() {
return {
selectTableShow: false,
//
loading: true,
//
showSearch: true,
//
spaceList: [],
//
spaceOptions: [],
//
title: "",
//
open: false,
//
queryParams: {
parentId: null,
projectId: null,
spaceName: null,
spaceCode: null,
spaceType: null
},
//
form: {},
//
rules: {},
spaceTypeOptions: [],
querySpaceOptions: [],
sourceId: "",
spaceDeviceTitle: ""
};
},
created() {
this.getDicts("space_type").then(response => {
this.spaceTypeOptions = response.data;
});
this.getQueryTreeselect();
this.getList();
},
methods: {
indexFormatter(val) {
return val + 1;
},
//
handleDetails(row) {
this.sourceId = row.spaceId;
this.spaceDeviceTitle = `[ ${row.spaceName} ] 空间--设备管理`;
this.selectTableShow = true;
},
//
statusFormat(row, column) {
return this.selectDictLabel(this.spaceTypeOptions, row.spaceType);
},
/** 查询项目空间列表 */
getList() {
this.loading = true;
this.queryParams.projectId = this.infoData.projectId;
listSpace(this.queryParams).then(response => {
this.spaceList = this.handleTree(response.data, "spaceId", "parentId");
this.loading = false;
});
},
/** 转换项目空间数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
}
return {
id: node.spaceId,
label: node.spaceName,
children: node.children
};
},
/** 查询部门下拉树结构 */
getTreeselect(param) {
listSpaceTree(param).then(response => {
this.spaceOptions = [];
const data = { id: 0, label: "顶级节点", children: [] };
// data.children = this.handleTree(response.data, "spaceId", "parentId");
data.children = response.data;
this.spaceOptions.push(data);
});
},
/** 查询部门下拉树结构 */
getQueryTreeselect() {
let param = {
projectId: this.infoData.projectId
};
listSpace(param).then(response => {
this.querySpaceOptions = [];
const data = { spaceId: 0, spaceName: "顶级节点", children: [] };
data.children = this.handleTree(response.data, "spaceId", "parentId");
this.querySpaceOptions.push(data);
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
parentId: null,
projectId: this.infoData.projectId,
spaceName: null,
spaceType: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
let param = {
projectId: this.infoData.projectId
};
this.getTreeselect(param);
this.open = true;
this.title = "添加项目空间";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
let param = {
projectId: this.infoData.projectId,
spaceId: row.spaceId
};
this.getTreeselect(param);
if (row != null) {
this.form.parentId = row.spaceId;
}
getSpace(row.spaceId).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改项目空间";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.spaceId != null) {
updateSpace(this.form).then(response => {
this.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addSpace(this.form).then(response => {
this.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
this.$confirm(
'是否确认删除项目空间编号为"' + row.spaceId + '"的数据项?',
"警告",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}
)
.then(function() {
return delSpace(row.spaceId);
})
.then(() => {
this.getList();
this.msgSuccess("删除成功");
});
}
}
};
</script>
<style lang="scss">
.e-project-space {
.eldialog-wrap {
.el-dialog__header {
border-bottom: 1px solid #747373;
}
.el-dialog__body {
padding: 0px;
height: calc(100vh - 200px);
overflow: auto;
}
.el-form {
padding: 20px;
padding-right: 40px;
}
.el-dialog__footer {
height: 60px;
border-top: 1px solid #747373;
text-align: right;
width: 100%;
padding: 0px;
padding-top: 15px;
.el-button + .el-button {
margin-right: 10px;
}
.el-button {
padding-top: 8px;
}
}
}
}
</style>

View File

@ -0,0 +1,353 @@
<template>
<div class="app-container">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="space_deviceList"
:default-sort="{prop: 'createTime', order: 'descending'}"
@sort-change="sortChange"
>
<el-table-column type="index" label="序号" align="center" :index="indexFormatter" width="80px"></el-table-column>
<el-table-column label="设备名称" align="left" width="200px" prop="deviceName" />
<el-table-column label="所属型号" align="left" prop="modelName" />
<el-table-column label="设备key" align="left" prop="deviceKey" />
<el-table-column label="设备类型" align="left" width="120px" prop="deviceTypeName" />
<el-table-column label="创建时间" sortable="custom" align="center" width="200" prop="createTime" />
<el-table-column label="操作" width="150" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-delete"
v-if="scope.row.deviceType !== 'MINIATURE_BREAKER'"
@click="handleDelete(scope.row)"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改空间设备对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px"></el-form>
<div slot="footer" class="dialog-footer">
<el-button size="mini" type="primary" @click="submitForm"> </el-button>
<el-button size="mini" @click="cancel"> </el-button>
</div>
</el-dialog>
<el-dialog
title="选择设备"
:visible.sync="selectTableShow"
width="75%"
top="10vh"
class="select-table-dialog"
:close-on-click-modal="false"
append-to-body
>
<select-table-wrap
v-if="selectTableShow"
:tableOption="tableSelectOption.tableOpt"
:queryOption="tableSelectOption.queryOpt"
:tableList="tableSelectOption.tableList"
@parentGetList="childGetList($event)"
:otherOption="tableSelectOption.otherOption"
@returnEvent="returnEvent($event)"
/>
<div slot="footer" class="dialog-footer">
<el-button size="mini" type="primary" @click="submitForm"> </el-button>
<el-button size="mini" @click="() =>{selectTableShow = false}"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
listSpace_device,
getSpace_device,
delSpace_device,
addSpace_device,
updateSpace_device,
exportSpace_device,
listProjectDevice
} from "@/api/iot/spaceDevice";
import { listSpaceDevice } from "@/api/iot/space";
import SelectTableWrap from "@/components/SelectTable/index";
export default {
name: "SpaceDeviceWrap",
props: ["sourceId", "projectId"],
components: {
SelectTableWrap
},
data() {
return {
selectTableShow: false,
tableSelectOption: {},
selectResult: {},
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
space_deviceList: [],
//
title: "",
//
open: false,
//
queryParams: {
spaceId: "",
pageNum: 1,
pageSize: 10,
orderByColumn: "createTime",
isAsc: "desc"
},
//
form: {},
//
rules: {}
};
},
created() {
this.getList();
},
methods: {
sortChange(column) {
const sort = {
isAsc: column.order === "descending" ? "desc" : "asc",
orderByColumn: column.prop
};
this.queryParams = Object.assign(this.queryParams, sort);
this.handleQuery();
},
indexFormatter(val) {
return (
val + 1 + (this.queryParams.pageNum - 1) * this.queryParams.pageSize
);
},
//
handleDetails() {
this.selectResult = {};
this.tableSelectOption = {
otherOption: {
tableType: "device"
},
queryOpt: {
disable: false,
labelWidth: "68px",
params: {
deviceName: "",
modelId: "",
parentId: 0,
deviceType: "GATEWAY_CONTROLLER"
},
page: {
pageSize: 10,
pageNum: 1,
total: 0
},
inline: true,
queryChilds: [
{
style: "",
placeholder: "设备名称",
clearable: true,
label: "设备名称",
type: "input",
key: "deviceName",
size: "small",
value: ""
}
]
},
tableOpt: {
loading: false,
rowKey: "deviceId",
selection: false,
maxHeight: "45vh",
childs: [
{
style: "",
label: "所属型号",
type: "",
prop: "modelName",
align: "left",
width: "",
"show-overflow-tooltip": false,
tempType: "span"
},
{
style: "",
label: "设备名称",
type: "",
prop: "deviceName",
align: "left",
width: "",
"show-overflow-tooltip": false,
tempType: "span"
},
{
style: "",
label: "设备Key",
type: "",
prop: "deviceKey",
align: "left",
width: "",
"show-overflow-tooltip": false,
tempType: "span"
},
{
style: "",
label: "设备类型",
type: "",
prop: "deviceTypeName",
align: "left",
width: "",
"show-overflow-tooltip": false,
tempType: "span"
},
{
style: "",
label: "创建时间",
type: "time",
prop: "createTime",
align: "center",
width: "180",
"show-overflow-tooltip": false,
tempType: "span"
}
],
tableList: {
type: Array
}
},
tableList: []
};
this.selectTableShow = true;
},
childGetList(data) {
console.log(data.param)
listProjectDevice(data.param).then(response => {
this.tableSelectOption.tableList = response.data;
// this.tableSelectOption.queryOpt.page.total = Number(response.total);
});
},
returnEvent(data) {
if (data.type === "dblclick") {
this.form.deviceId = data.value.deviceId;
this.submitForm();
} else if (data.type === "click") {
this.form.deviceId = data.value.deviceId;
}
},
/** 查询空间设备列表 */
getList() {
this.loading = true;
this.queryParams.spaceId = this.sourceId;
listSpace_device(this.queryParams).then(response => {
this.space_deviceList = response.rows;
this.total = response.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
spaceId: this.sourceId,
deviceId: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.spaceId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.handleDetails();
},
/** 提交按钮 */
submitForm() {
addSpace_device(this.form).then(response => {
this.msgSuccess("新增成功");
this.selectTableShow = false;
this.getList();
});
},
/** 删除按钮操作 */
handleDelete(row) {
const spaceIds = row.deviceId;
this.$confirm("是否删除该选项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(function() {
return delSpace_device(spaceIds);
})
.then(() => {
this.getList();
this.msgSuccess("删除成功");
});
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$confirm("是否确认导出所有空间设备数据项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(function() {
return exportSpace_device(queryParams);
})
.then(response => {
this.download(response.msg);
});
}
}
};
</script>

View File

@ -0,0 +1,386 @@
<template>
<div class="app-container">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
>新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="project_tenantList" :default-sort="{prop: 'expirationTime', order: 'descending'}"
@sort-change="sortChange">
<el-table-column type="index" label="序号" align="center" :index="indexFormatter" width="80px"></el-table-column>
<el-table-column label="企业名称" align="center" prop="tenantName" />
<el-table-column label="状态" align="center" prop="status">
<template slot-scope="scope">
<span v-text="scope.row.status === '0' ? '正常' : '停用'"></span>
</template>
</el-table-column>
<el-table-column label="有效期" align="center" sortable="custom" prop="expirationTime" width="160" />
<el-table-column
label="操作"
align="center"
width="200px"
class-name="small-padding fixed-width"
>
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
>解除租户</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改项目租户关系对话框 -->
<!-- <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px"></el-form>
<div slot="footer" class="dialog-footer">
<el-button size="mini" type="primary" @click="submitForm"> </el-button>
<el-button size="mini" @click="cancel"> </el-button>
</div>
</el-dialog> -->
<el-dialog
title="选择租户"
:visible.sync="selectTableShow"
width="50%"
top="10vh"
class="select-table-dialog"
:close-on-click-modal="false"
append-to-body
>
<select-table-wrap
v-if="selectTableShow"
:tableOption="tableSelectOption.tableOpt"
:queryOption="tableSelectOption.queryOpt"
:tableList="tableSelectOption.tableList"
@parentGetList="childGetList($event)"
:otherOption="tableSelectOption.otherOption"
@returnEvent="returnEvent($event)"
/>
<div slot="footer" class="dialog-footer">
<el-button size="mini" type="primary" @click="submitForm"> </el-button>
<el-button size="mini" @click="() =>{selectTableShow = false}"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
listProject_tenant,
getProject_tenant,
delProject_tenant,
addProject_tenant,
updateProject_tenant,
exportProject_tenant,
listUserdByProjectId
} from "@/api/iot/projectTenant";
import SelectTableWrap from "@/components/SelectTable/index";
export default {
name: "EObjectTenant",
components: {
SelectTableWrap
},
props: ["infoData"],
data() {
return {
selectTableShow: false,
tableSelectOption: {},
selectResult: {},
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
project_tenantList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
orderByColumn: "expirationTime",
isAsc: "desc"
},
//
form: {},
//
rules: {},
//
statusOptions: [],
tenantTypeOptions: []
};
},
created() {
this.getDicts("sys_tenant_type").then(response => {
this.tenantTypeOptions = response.data;
});
this.getDicts("sys_normal_disable").then(response => {
this.statusOptions = response.data;
});
this.getList();
},
methods: {
sortChange(column) {
const sort = {
isAsc: column.order === "descending" ? "desc" : "asc",
orderByColumn: column.prop
};
this.queryParams = Object.assign(this.queryParams, sort);
this.handleQuery();
},
indexFormatter(val) {
return (
val + 1 + (this.queryParams.pageNum - 1) * this.queryParams.pageSize
);
},
//
handleDetails() {
this.selectResult = {};
this.tableSelectOption = {
otherOption: {
tableType: "device"
},
queryOpt: {
disable: false,
labelWidth: "68px",
params: {
projectId: this.infoData.projectId,
tenantName: ""
},
page: {
pageSize: 10,
pageNum: 1,
total: 0
},
inline: true,
queryChilds: [
{
style: "",
placeholder: "企业名称",
clearable: true,
label: "企业名称",
type: "input",
key: "tenantName",
size: "small",
value: ""
}
]
},
tableOpt: {
loading: false,
rowKey: "deviceId",
selection: false,
maxHeight: "45vh",
childs: [
{
style: "",
label: "企业名称",
type: "",
prop: "tenantName",
align: "left",
width: "",
"show-overflow-tooltip": false,
tempType: "span"
},
{
style: "",
label: "企业法人",
type: "",
prop: "corporation",
align: "left",
width: "",
"show-overflow-tooltip": false,
tempType: "span"
},
{
style: "",
label: "状态",
type: "",
prop: "status",
align: "left",
width: "",
"show-overflow-tooltip": false,
tempType: "text1"
},
{
style: "",
label: "有效期",
type: "time",
prop: "expirationTime",
align: "center",
width: "180",
"show-overflow-tooltip": false,
tempType: "span"
}
],
tableList: {
type: Array
}
},
tableList: []
};
this.selectTableShow = true;
},
childGetList(data) {
listUserdByProjectId(Object.assign(data.page, data.param)).then(
response => {
this.tableSelectOption.tableList = response.rows;
this.tableSelectOption.queryOpt.page.total = Number(response.total);
}
);
},
returnEvent(data) {
if (data.type === "dblclick") {
this.form.tenantId = data.value.tenantId;
this.submitForm();
} else if (data.type === "click") {
this.form.tenantId = data.value.tenantId;
}
},
//
statusFormat(row, column) {
var rulesStr = "";
this.statusOptions.forEach(v => {
if (v.dictValue === row.status) {
rulesStr = v.dictLabel;
}
});
return rulesStr;
},
tenantTypeFormat(row, column) {
var rulesStr = "";
this.tenantTypeOptions.forEach(v => {
if (v.dictValue === row.status) {
rulesStr = v.dictLabel;
}
});
return rulesStr;
},
/** 查询项目租户关系列表 */
getList() {
this.loading = true;
this.queryParams.projectId = this.infoData.projectId;
listProject_tenant(this.queryParams).then(response => {
this.project_tenantList = response.rows;
this.total = response.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
projectId: this.infoData.projectId,
tenantId: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.projectId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.handleDetails();
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const projectId = row.projectId || this.ids;
getProject_tenant(projectId).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改项目租户关系";
});
},
/** 提交按钮 */
submitForm() {
addProject_tenant(this.form).then(response => {
this.msgSuccess("新增成功");
this.selectTableShow = false;
this.getList();
});
},
/** 删除按钮操作 */
handleDelete(row) {
const projectIds = row.projectId;
const tenantId = row.tenantId;
this.$confirm("是否删除该选项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(function() {
return delProject_tenant(projectIds, tenantId);
})
.then(() => {
this.getList();
this.msgSuccess("删除成功");
});
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$confirm("是否确认导出所有项目租户关系数据项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(function() {
return exportProject_tenant(queryParams);
})
.then(response => {
this.download(response.msg);
});
}
}
};
</script>

View File

@ -0,0 +1,19 @@
<template>
<div class="e-project-manage">
项目管理
</div>
</template>
<script>
export default {
name: 'EProjectManage',
props: {
projectInfo: {
type: Object,
require: true
}
},
data() {
return {}
}
}
</script>

View File

@ -0,0 +1,169 @@
<template>
<div class="project-e-today-ratio">
<div class="bd-conter">
<div class="echarts-list-wrap">
<div v-for="item in rchartsList" :key="item.valueKeys">
<echarts-gauge-wrap
:styles="item.styles"
:colorList="item.colorList"
:eId="item.valueKeys"
:option="tempResult[item.valueKeys]"
></echarts-gauge-wrap>
<div class="title">{{ item.title }}</div>
</div>
</div>
<div class="info-tb-block">
<div>
<div style="font-weight: 600;">{{ alarmTB }}%</div>
<div>同比</div>
</div>
<div>
<div style="font-weight: 600;">{{ warningTB }}%</div>
<div>同比</div>
</div>
</div>
</div>
</div>
</template>
<script>
import EchartsGaugeWrap from "./EEchartsGauge";
export default {
name: "ETodayRatio",
components: {
EchartsGaugeWrap,
},
props: ["result"],
data() {
return {
tempResult: {
q1: 0,
q2: 0,
q3: 0,
q4: 0,
},
alarmTB: 0,
warningTB: 0,
rchartsList: [
{
styles: "width: 100%; height: 100%;",
colorList: ["#27d0ec"],
valueKeys: "q1",
title: "今日报警",
},
{
styles: "width: 100%; height: 100%;",
colorList: ["#fdc46e"],
valueKeys: "q2",
title: "昨日报警 ",
},
{
styles: "width: 100%; height: 100%;",
colorList: ["#27d0ec"],
valueKeys: "q3",
title: "今日预警",
},
{
styles: "width: 100%; height: 100%;",
colorList: ["#fdc46e"],
valueKeys: "q4",
title: "昨日预警",
},
],
title: "最近2天警情同比",
};
},
watch: {
result: {
handler: function (val, oldVal) {
if (val) {
this.tempResult = {};
for (var i = 0; i < val.length; i++) {
if (val[i]["alarmDivide"] === "ALARM") {
this.tempResult["q1"] = val[i]["todayAlarm"];
this.tempResult["q2"] = val[i]["yesterdayAlarm"];
this.alarmTB = val[i]["dayOnDay"];
} else if (val[i]["alarmDivide"] === "WARNING") {
this.tempResult["q3"] = val[i]["todayAlarm"];
this.tempResult["q4"] = val[i]["yesterdayAlarm"];
this.warningTB = val[i]["dayOnDay"];
}
}
}
},
deep: true,
},
},
};
</script>
<style lang="scss">
.project-e-today-ratio {
width: 100%;
display: flex;
flex-wrap: wrap;
.bd-conter {
width: 100%;
height: 220px;
background-size: contain;
padding-top: 10px;
padding: 5px;
display: flex;
padding: 0;
.echarts-list-wrap {
width: 80%;
height: 200px;
height: 100%;
display: flex;
flex-wrap: wrap;
> div {
width: 50%;
height: calc(100% / 2);
display: flex;
flex-wrap: wrap;
justify-content: center;
font-size: 14px;
font-family: "Source Han Sans CN";
font-weight: 400;
> .title {
position: relative;
top: -10px;
width: 100%;
display: flex;
height: 15px;
justify-content: center;
font-size: 12px;
font-weight: 400;
color: #344567;
}
}
}
.info-tb-block {
height: 100%;
display: flex;
width: 20%;
flex-wrap: wrap;
align-items: center;
font-size: 16px;
cursor: default;
> div {
width: 100%;
// height: 50%;
display: flex;
flex-wrap: wrap;
align-items: inherit;
justify-content: center;
> div {
width: 100%;
height: 20px;
display: flex;
justify-content: center;
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #344567;
}
}
}
}
}
</style>

View File

@ -0,0 +1,205 @@
<template>
<div class="project-e-trend-census">
<div class="trend-conter">
<e-echarts-line
ref="echartsLineTrend"
:styles="echartsOption.styles"
:colorList="echartsOption.colorList"
:eId="echartsOption.eId"
:option="resultOption"
/>
</div>
</div>
</template>
<script>
import EEchartsLine from "@/components/Echarts/EEchartsLine";
import * as echarts from "echarts";
export default {
name: "ETrendCensus",
components: {
EEchartsLine
},
props: ["result"],
data() {
return {
echartsOption: {
styles: "width: 100%; height: 100%;",
colorList: ["#FC6A16", "#1EEEFF"],
eId: "trendEchartsLine",
},
typeName: "alarm",
resultOption: {
color: ["#FC6A16", "#1EEEFF"],
title: {
text: "",
show: false,
},
tooltip: {
trigger: "axis",
},
legend: {
top: 5,
right: 5,
data: ["报警", "预警"],
// icon: "circle",
textStyle: {
color: "#344567",
fontSize: 12,
fontWeight: 400
},
},
grid: {
left: "20",
right: "20",
bottom: "5",
top: 30,
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
show: true,
data: [],
splitLine: {
show: false,
},
axisLabel: {
show: true,
textStyle: {
// color: "#E8E8E8",
},
},
},
yAxis: {
type: "value",
splitLine: {
show: false,
},
axisLabel: {
show: true,
textStyle: {
// color: "#E8E8E8",
},
},
},
series: [
{
name: "报警",
type: "line",
smooth: true,
symbolSize: 2,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "rgba(252,106,22,0.8)",
},
{
offset: 1,
color: "rgba(252,106,22,0.1)",
},
]),
},
markPoint: {
symbolSize: 35,
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' }
]
},
label: {
show: false,
formatter: function (params) {
return echarts.format.formatTime("yyyy-MM-dd", params.value);
},
backgroundColor: "#7581BD",
},
data: [],
},
{
name: "预警",
type: "line",
markPoint: {
symbolSize: 35,
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' }
]
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "rgba(30,238,255,0.8)",
},
{
offset: 1,
color: "rgba(30,238,255,0.1)",
},
]),
},
smooth: true,
symbolSize: 2,
data: [],
},
],
},
};
},
watch: {
result: {
handler: function (val, oldVal) {
this.resultOption.xAxis.data = val["name"];
this.resultOption.series[0].data = val["alarm"];
this.resultOption.series[1].data = val["warning"];
this.updateEcharts();
},
deep: true,
},
},
methods: {
//
updateEcharts() {
this.$refs.echartsLineTrend.updateEchart();
},
},
};
</script>
<style lang="scss">
.project-e-trend-census {
width: 100%;
display: flex;
flex-wrap: wrap;
.trend-conter {
width: 100%;
height: 200px;
display: flex;
flex-wrap: wrap;
justify-content: center;
padding-top: 10px;
background-size: contain;
padding: 0;
.tabs-block {
width: 305px;
height: 38px;
background: #bfbfbf94;
display: flex;
color: #fff;
> div {
width: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
letter-spacing: 7px;
cursor: default;
}
.div-select {
background: #0010ff;
}
}
}
}
</style>

View File

@ -0,0 +1,258 @@
<template>
<div class="project-e-type-census">
<div class="conter-block">
<div class="tabs-block">
<div
@click="typeName = 'alarm'"
:class="typeName === 'alarm' ? 'div-select' : ''"
>
报警
</div>
<div
@click="typeName = 'waraing'"
:class="typeName === 'waraing' ? 'div-select' : ''"
>
预警
</div>
</div>
<div v-show="!(templist[typeName] && templist[typeName].length > 0)">
暂无数据
</div>
<echarts-pie-wrap
v-show="(templist[typeName] && templist[typeName].length > 0)"
ref="echartsPieType"
style="height: calc(100% - 38px);"
:styles="echartsOption.styles"
:colorList="echartsOption.colorList"
:eId="echartsOption.eId"
:option="templist[typeName]"
></echarts-pie-wrap>
<div v-show="(templist[typeName] && templist[typeName].length > 0)" class="echarts-legend-c">
<div class="legend-for">
<div
class="legend-item"
v-for="(item, idx) in templist[typeName]"
:key="idx"
>
<div
class="color-block"
:style="`background: ${echartsOption.colorList[idx]};`"
></div>
<span class="title-block" :title="item.name" style="
color: #6B778C;
display: inline-block;
text-overflow: ellipsis;
overflow: hidden;
width: 100%;
white-space: nowrap;
">
{{
item.name.length > 10
? item.name.substr(0, 10) + "..."
: item.name
}}
</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import EchartsPieWrap from "./EEchartsPie";
export default {
name: "ETypeCensus",
components: {
EchartsPieWrap
},
props: ["result"],
data() {
return {
resultList: [
{ value: 40, name: "rose 1" },
{ value: 38, name: "rose 2" },
{ value: 32, name: "rose 3" },
{ value: 30, name: "rose 4" },
],
templist: {
alarm: [],
waraing: [],
},
echartsOption: {
styles: "width: 80%; height: calc(100%);",
colorList: [
"#00deff",
"#ff643e",
"#8882f7",
"#01b3d0",
"#ff9724",
"#168efe",
"#00c279",
"#ffce2a",
"#ff88a7",
"#3785b3",
"#2e966b",
"#8d68d8",
"#c77856",
"#ff4df9",
"#0066ff",
"#788a00",
"#73c0de",
"#91cc75",
"#5470c6",
"#ff69d6",
"#d9815c",
"#00b604",
"#b2942e",
"#b2942e",
"#0656da",
"#ff4e00",
"#387289",
"#5aa1bd",
"#5443f9",
"#708694",
"#cd1fc7",
"#ff00ae",
"#a26f00",
"#277900",
"#00b604",
"#ac29ff",
"#ff3737",
"#eae700",
"#b1af01",
],
eId: "typeEchartsPie",
},
title: "类型分布统计",
typeName: "alarm",
};
},
watch: {
typeName(val, old) {
if (val) {
this.updateEcharts();
}
},
result: {
handler: function (list, oldVal) {
this.templist = {
alarm: [],
waraing: [],
};
if (list) {
for (var i = 0; i < list.length; i++) {
// console.log(list[i]['typeCode'].indexOf('a'))
if (list[i]["typeCode"].indexOf("a") === 0) {
this.templist["alarm"].push({
value: list[i].alarmTotal,
name: list[i].typeName,
});
} else if (list[i]["typeCode"].indexOf("w") === 0) {
this.templist["waraing"].push({
value: list[i].alarmTotal,
name: list[i].typeName,
});
}
}
}
this.updateEcharts();
},
deep: true,
},
},
methods: {
//
updateEcharts() {
if(this.$refs.echartsPieType) {
this.$refs.echartsPieType.updateEchart();
} else {
console.log('err_log:--', 'this.$refs.echartsPieType 组件未找到!')
}
},
},
};
</script>
<style lang="scss">
.project-e-type-census {
width: 100%;
height: 238px;
display: flex;
flex-wrap: wrap;
.conter-block {
width: 100%;
height: 238px;
display: flex;
flex-wrap: wrap;
justify-content: center;
// padding-top: 10px;
background-size: contain;
.tabs-block {
width: 305px;
height: 28px;
display: flex;
background: #efefef;
color: #344567;
border-radius: 14px;
overflow: hidden;
> div {
width: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
letter-spacing: 7px;
cursor: default;
}
.div-select {
color: #fff;
background: #1890ff;
}
}
.echarts-legend-c {
width: 40%;
height: calc(100% - 38px);
display: flex;
flex-wrap: wrap;
.legend-for {
width: 100%;
width: 100%;
height: calc(100% - 25px);
overflow: auto;
.legend-item {
height: 25px;
width: 100%;
display: flex;
align-items: center;
font-size: 14px;
font-weight: 400;
color: #ffffff;
line-height: 24px;
.color-block {
width: 9px;
height: 8px;
margin-right: 10px;
}
}
}
}
}
.echarts-legend-c .legend-for::-webkit-scrollbar {
/*滚动条整体样式*/
width: 4px; /*高宽分别对应横竖滚动条的尺寸*/
height: 3px;
}
.echarts-legend-c .legend-for::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
border-radius: 10px;
box-shadow: inset 0 0 5px #004eb0;
background: #004eb0;
}
.echarts-legend-c .legend-for::-webkit-scrollbar-track {
/*滚动条里面轨道*/
box-shadow: inset 0 0 5px #042764;
border-radius: 10px;
background: #042764;
}
}
</style>

View File

@ -0,0 +1,451 @@
<template>
<div class="project-detail-v2"
id="project-detail-v2-id"
ref="project-detail-v2-id"
:style="{
transformOrigin: 'center top',
transform: `scale(${scalseNumX}, ${scalseNumY})`,
opacity: 1,
transition: `all 0.5s ease 0s`,
}"
>
<div :class="isFoldRight ? 'block-lift block-right-fold':'block-lift'">
<div class="container-title">
<div class="title-left">
<span>{{ infoData.projectName }}</span>
<i class="el-icon-arrow-left icon-c"></i>
<i class="el-icon-arrow-right icon-c"></i>
</div>
<div class="title-but">
<div class="but-home" @click="handleLinkToTable">
<i class="el-icon-d-arrow-left icon-c"></i>
<span class="but-title">返回列表</span>
</div>
<div class="but-tz" @click="handleFoldRight">
<i class="el-icon-back icon-c"></i>
<i class="el-icon-right icon-c"></i>
</div>
</div>
</div>
<div class="container-info">
<div class="info-left">
<el-descriptions class="margin-top" title="" :column="2">
<el-descriptions-item label="行业类型:">{{
statusFormat(infoData)
}}</el-descriptions-item>
<el-descriptions-item label="联系人1">{{
infoData.contractName
}}</el-descriptions-item>
<el-descriptions-item label="联系人2"
>林工1586000000</el-descriptions-item
>
<el-descriptions-item label="联系人2">
{{ infoData["projectDeviceName"] }}
</el-descriptions-item>
<el-descriptions-item label="项目地址:">{{
infoData.projectAddress
}}</el-descriptions-item>
</el-descriptions>
</div>
<div class="info-divider"></div>
<div class="info-right">
<div>
<e-census-cards :propList="censusCardList" :result="censusResult" />
</div>
</div>
</div>
<div class="container-main">
<e-nav-menu
v-model="activeName"
:activeList="activeList"
@input="
(data) => {
activeName = data;
}
"
/>
<div class="main-block">
<component :is="activeName" :projectInfo="infoData" :projectTypeOptions="projectTypeOptions"></component>
</div>
</div>
</div>
<div class="block-right" v-if="!isFoldRight">
<e-simple-card title="最近2 天警情同比" class="today-ratio-card">
<template slot="cardBody">
<e-today-ratio :result="resultInfo['warningAnalysisStatisticsVo']"/>
</template>
</e-simple-card>
<e-simple-card
title="类型分布统计"
class="type-census-card"
>
<template slot="cardBody">
<e-type-census
:result="resultInfo['alarmCategoriesList'] || []"
/>
</template>
</e-simple-card>
<e-simple-card
title="报警预警趋势"
class="trend-census-card"
>
<template slot="cardBody">
<e-trend-census :result="resultInfo['alarmChartDataVo'] || []"/>
</template>
</e-simple-card>
</div>
</div>
</template>
<script>
import { getProject } from "@/api/iot/project";
import { getWarningAnalysis, appProjectList } from "@/api/app";
import ECensusCards from "./ECensusCards";
import ElDescriptions from "@/components/Edescriptions";
import ElDescriptionsItem from "@/components/Edescriptions/src/descriptions-item";
import ESimpleCard from "@/components/Cards/index";
import ENavMenu from "./ENavMenu";
import EProjectManage from "./EProjectManage";
import ETodayRatio from "./ETodayRatio";
import ETypeCensus from './ETypeCensus'
import ETrendCensus from './ETrendCensus'
import EObjectContainer from './EObjectContainer'
import EDeviceManage from './DeviceManage/index'
export default {
name: "projectDetailV2",
components: {
ECensusCards,
ElDescriptions,
ElDescriptionsItem,
ENavMenu,
EProjectManage,
ESimpleCard,
ETodayRatio,
ETypeCensus,
ETrendCensus,
EObjectContainer,
EDeviceManage
},
props: {
sourceId: {
type: [String, Number],
require: true,
},
},
data() {
return {
stateSourceId: null,
resultInfo: {},
activeName: "EObjectContainer",
activeList: [
{
label: "项目信息",
key: "EObjectContainer",
},
{
label: "设备管理",
key: "EDeviceManage",
},
],
projectTypeOptions: [],
infoData: {},
censusResult: {
device: 3555,
},
censusCardList: [
{
label: "设备总数",
key: "device",
},
{
label: "在线设备数",
key: "device",
},
{
label: "离线设备数",
key: "device",
},
{
label: "今日报警数",
key: "device",
},
{
label: "本月报警数",
key: "device",
},
],
isFoldRight: false,
scalseNumX: 1,
scalseNumY: 1,
};
},
created() {
this.stateSourceId = this.sourceId;
this.getInfoByProjectId();
this.activeName = "EObjectContainer";
this.getDicts("project_industry").then((response) => {
this.projectTypeOptions = response.data;
});
this.warningAnalysisList();
},
switch: {
sourceId(val) {
console.log(val);
this.stateSourceId = this.sourceId;
this.getInfoByProjectId();
this.warningAnalysisList();
},
},
mounted() {
//
// this.resize_window();
// this.thisScrollTopY = document.getElementById("con_lf_top_div").scrollTop;
// document.getElementById("con_lf_top_div").style.background = "#010e45";
// document.getElementById("con_lf_top_div").style.height =
// "calc(100vh - 0px)";
// document.getElementById("con_lf_top_div").style.minHeight =
// "calc(100vh - 0px)";
// document.getElementById("con_lf_top_div").style.overflow = "hidden";
// document.getElementById("con_lf_top_div").scrollTop = 0;
// window.addEventListener("resize", () => {
// this.resize_window();
// });
},
methods: {
resize_window() {
// if (this.fullscreen) {
// this.scalseNumX = Number(document.documentElement.clientWidth / 1682);
// this.scalseNumY = Number(document.documentElement.clientHeight / 812);
// } else {
// if (document.documentElement.clientWidth < 993) {
// this.scalseNumX =
// Number(document.documentElement.clientWidth / 1682) -
// (Cookies.get("sidebarStatus") !== "0" ? Number(200 / 1920) : 0);
// } else {
// this.scalseNumX =
// Number(document.documentElement.clientWidth / 1920) -
// (Cookies.get("sidebarStatus") !== "0"
// ? Number(200 / 1920)
// : Number(54 / 1920));
// }
// this.scalseNumY = Number(document.documentElement.clientHeight / 1080);
// }
},
//
handleLinkToTable() {
this.$emit('linkToTable')
},
//
handleFoldRight() {
this.isFoldRight = !this.isFoldRight
this.$forceUpdate()
},
//
warningAnalysisList() {
getWarningAnalysis({
projectId: this.stateSourceId,
}).then((res) => {
this.resultInfo = res.data;
});
},
//
statusFormat(row, column) {
return this.selectDictLabel(this.projectTypeOptions, row.industry);
},
getInfoByProjectId() {
getProject(this.stateSourceId).then((response) => {
this.infoData = response.data;
});
},
},
};
</script>
<style lang="scss">
.project-detail-v2 {
width: 100%;
height: 100%;
height: calc(100vh - 125px);
display: flex;
.block-lift {
width: calc(100% - 300px - 20px);
height: 100%;
display: flex;
flex-wrap: wrap;
.container-title {
height: 45px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.title-left {
width: 50%;
display: flex;
justify-content: flex-start;
height: 100%;
align-items: center;
> span {
font-size: 24px;
font-family: "Source Han Sans CN";
font-weight: 600;
color: #172b4d;
}
.icon-c {
font-size: 18px;
color: #6b778c;
font-weight: 600;
margin-top: 2px;
margin-left: 15px;
}
}
.title-but {
width: 50%;
display: flex;
justify-content: flex-end;
.but-home {
width: 90px;
height: 30px;
background: #f4f5f7;
border-radius: 5px;
color: #6b778c;
justify-content: center;
align-items: center;
display: flex;
margin-right: 10px;
.icon-c {
font-size: 12px;
font-weight: 600;
margin-top: 2px;
}
.but-title {
font-size: 12px;
font-family: "Source Han Sans CN";
font-weight: 600;
margin-left: 5px;
cursor: default;
}
}
.but-home:hover {
background: #1890ff;
color: #fff;
}
.but-tz {
width: 30px;
height: 30px;
background: #f4f5f7;
border-radius: 5px;
justify-content: center;
align-items: center;
display: flex;
margin-right: 0px;
font-weight: 600;
color: #6b778c;
.icon-c {
font-size: 12px;
font-weight: 600;
margin-top: 2px;
}
}
.but-tz:hover {
background: #1890ff;
color: #fff;
}
}
}
.container-info {
height: 104px;
width: 100%;
background: #f4f5f7;
border-radius: 5px;
margin-top: 5px;
display: flex;
.info-left {
width: calc(40%);
padding: 8px 20px;
display: flex;
justify-content: center;
align-items: center;
.margin-top {
width: 100%;
}
}
.info-divider {
width: 1px;
height: 105px;
background: #e8e8e8;
}
.info-right {
width: calc(100% - 1px - 40%);
display: flex;
height: 104px;
justify-content: center;
align-items: center;
}
}
.container-main {
width: calc(100% - 10px);
height: calc(100vh - 286px);
margin-left: 10px;
margin-top: 15px;
display: flex;
border: 1px solid #e8e8e8;
border-radius: 5px;
.main-block {
padding: 15px;
width: calc(100% - 150px);
height: 100%;
}
}
}
.block-right-fold {
width: 100%;
}
.block-right {
width: 300px;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
padding: 5px 0;
margin-left: 20px;
.today-ratio-card {
height: 270px;
margin-bottom: 20px;
.e-simple-card__body {
padding: 10px;
}
}
.type-census-card {
height: 245px;
margin-bottom: 20px;
.e-simple-card__body {
padding: 10px;
}
}
.trend-census-card {
height: 260px;
margin-bottom: 20px;
.e-simple-card__body {
padding: 10px;
}
}
}
.el-descriptions__body {
background-color: #fff0;
}
.margin-top {
margin-top: 0;
}
.el-descriptions .el-descriptions-item__cell {
padding-bottom: 6px;
}
.el-descriptions .is-bordered .el-descriptions-item__cell {
padding: 5px 5px;
// border: 0px solid #ebeef5;
}
.el-descriptions-item__label.is-bordered-label {
background: #fafafa00;
}
}
</style>

View File

@ -113,7 +113,7 @@ import ModelOat from "./modelOat";
import ChildDevice from "./childDevice"; import ChildDevice from "./childDevice";
export default { export default {
name: "DetailsWrap", name: "DetailsWrap",
props: ["sourceId", "isTenant"], props: ["sourceId", "isTenant", 'isPersonal'],
components: { components: {
InfoWrap, InfoWrap,
DeviceLog, DeviceLog,

View File

@ -19,7 +19,7 @@
> >
<el-input v-if="updateState === true" v-model="temp.deviceName" /> <el-input v-if="updateState === true" v-model="temp.deviceName" />
<el-button <el-button
v-if="updateState === false && !isTenant" v-if="updateState === false"
type="text" type="text"
@click.stop="handleUpdate(infoData)" @click.stop="handleUpdate(infoData)"
>编辑</el-button >编辑</el-button