detail.vue 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124
  1. <template>
  2. <div class="page">
  3. <div class="content">
  4. <div class="cont_box">
  5. <div class="title">设备:{{ detail.name }}</div>
  6. <div class="pro-status"><span :class="developer_status == 2 ? 'on' : 'off'"></span>{{ developer_status == 2 ? '在线'
  7. : '离线' }}</div>
  8. <!-- <div class="pro-option" @click="CkOption">{{ developer_status == 2 ? '下线' : '上线' }}</div> -->
  9. </div>
  10. </div>
  11. <div class="content-box">
  12. <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
  13. <el-tab-pane label="运行状态" name="3">
  14. <div style=" display: flex; padding: 10px;flex-wrap: wrap;">
  15. <div class="ant-card">
  16. <div class="ant-card-body">
  17. <div class="cardflex">
  18. <div>设备状态</div>
  19. <div @click="getrunData()" style="cursor: pointer;">
  20. <el-icon style="font-size: 18px;">
  21. <ele-Refresh />
  22. </el-icon>
  23. </div>
  24. </div>
  25. <div class="statusname" v-if="areaData.status == 0">未启用</div>
  26. <div class="statusname" v-if="areaData.status == 1">离线</div>
  27. <div class="statusname" v-if="areaData.status == 2">在线</div>
  28. <div class="cardflex comtest">
  29. <div> 数据时间</div>
  30. <div>{{ areaData.lastOnlineTime || '未启用' }}</div>
  31. </div>
  32. </div>
  33. </div>
  34. <div class="ant-card" v-for="(item, index) in areaData.properties" :key="index">
  35. <div class="ant-card-body">
  36. <div class="cardflex">
  37. <div>{{ item.name }}</div>
  38. <div style="cursor: pointer;">
  39. <el-icon style="font-size: 18px;" @click="getrunData()">
  40. <ele-Refresh />
  41. </el-icon>
  42. <el-icon style="font-size: 18px;margin-left: 10px;" @click="onOpenListDetail(item)">
  43. <ele-Expand />
  44. </el-icon>
  45. </div>
  46. </div>
  47. <div class="statusname" v-if="item.type != 'object'">
  48. {{ getValueText(item.key, item.value) }}
  49. <!-- {{ item.value }}{{ item.unit }} -->
  50. </div>
  51. <div v-else>
  52. <div class="oblist" v-for="(vare, name) in item.value">
  53. <div> {{ getStatusText(name, vare) }}</div>
  54. <!-- <div class="name">{{ name }}:</div>
  55. <div class="name">{{ vare }}</div> -->
  56. </div>
  57. </div>
  58. <div class="">
  59. <devantd :json="item.list" :antdid="item.key" v-if="item.type == 'int' || item.type == 'float' || item.type == 'string'" />
  60. </div>
  61. </div>
  62. </div>
  63. </div>
  64. </el-tab-pane>
  65. <el-tab-pane label="设备信息" name="1">
  66. <div class="pro-box">
  67. <div class="protitle">设备信息</div>
  68. <div>
  69. <el-button size="small" type="primary" v-auth="'edit'" @click="onOpenEditDic(detail)">编辑</el-button>
  70. </div>
  71. </div>
  72. <el-descriptions class="margin-top" :column="3" border>
  73. <el-descriptions-item label="设备标识">{{ detail.key }}</el-descriptions-item>
  74. <el-descriptions-item label="设备名称">{{ detail.name }}</el-descriptions-item>
  75. <el-descriptions-item label="所属产品">
  76. <router-link :to="'/iotmanager/device/product/detail/' + prodetail.id" class="link-type">{{
  77. detail.productName }} </router-link>
  78. </el-descriptions-item>
  79. <el-descriptions-item label="消息协议">{{ prodetail.messageProtocol }}</el-descriptions-item>
  80. <el-descriptions-item label="链接协议">{{ prodetail.transportProtocol }}</el-descriptions-item>
  81. <el-descriptions-item label="设备类型">{{ prodetail.deviceType }}</el-descriptions-item>
  82. <el-descriptions-item label="固件版本">{{ detail.version }}</el-descriptions-item>
  83. <el-descriptions-item label="注册时间">{{ detail.updatedAt }}</el-descriptions-item>
  84. <el-descriptions-item label="最后上线时间">{{ detail.lastOnlineTime || '' }}</el-descriptions-item>
  85. <el-descriptions-item label="详细地址">{{ detail.address }}</el-descriptions-item>
  86. <el-descriptions-item label="说明">{{ detail.desc }}</el-descriptions-item>
  87. </el-descriptions>
  88. <div class="flex" style="margin-top: 20px;">
  89. <el-input type="number" style="width: 380px;margin-right: 20px;" v-model.number="detail.onlineTimeout">
  90. <template #prepend>设备超时时间</template>
  91. <template #append>秒</template>
  92. </el-input>
  93. <el-button type="primary" @click="onlineTimeoutUpdate">
  94. <el-icon style="font-size: 18px;"><ele-Refresh /></el-icon>更新</el-button>
  95. </div>
  96. </el-tab-pane>
  97. <el-tab-pane label="物模型" name="2">
  98. <div class="wu-box">
  99. <el-tabs type="border-card" v-model="activetab" @tab-click="wuhandleClick">
  100. <el-tab-pane label="属性定义" name="attr">
  101. <div class="wu-title">
  102. <div class="title">属性定义</div>
  103. <div>
  104. <el-button size="small" type="primary" v-auth="'add'" @click="onOpenEditAttr()">添加</el-button>
  105. </div>
  106. </div>
  107. <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'attr'">
  108. <el-table-column label="属性标识" align="center" prop="key" />
  109. <el-table-column label="属性名称" prop="name" show-overflow-tooltip />
  110. <el-table-column prop="valueType" label="数据类型" width="100" align="center">
  111. <template #default="scope">
  112. <span>{{ scope.row.valueType.type }}</span>
  113. </template>
  114. </el-table-column>
  115. <el-table-column prop="decimals" label="精度" width="60" align="center">
  116. <template #default="scope">
  117. <span>{{ scope.row.valueType.decimals }}</span>
  118. </template>
  119. </el-table-column>
  120. <el-table-column prop="unit" label="单位" width="60" align="center">
  121. <template #default="scope">
  122. <span>{{ scope.row.valueType.unit }}</span>
  123. </template>
  124. </el-table-column>
  125. <el-table-column prop="accessMode" label="是否只读" width="120" align="center">
  126. <template #default="scope">
  127. <el-tag type="info" size="small" v-if="scope.row.accessMode">只读</el-tag>
  128. <el-tag type="success" size="small" v-else>读写</el-tag>
  129. </template>
  130. </el-table-column>
  131. <el-table-column label="说明" prop="desc" show-overflow-tooltip />
  132. <el-table-column label="操作" width="300" align="center" fixed="right">
  133. <template #default="scope">
  134. <el-button size="small" text type="warning" v-auth="'edit'" @click="onEditAttr(scope.row)">修改</el-button>
  135. <el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'attr')">删除</el-button>
  136. <el-button size="small" text type="primary" v-auth="'edit'" @click="setAttr(scope.row)">设置属性</el-button>
  137. </template>
  138. </el-table-column>
  139. </el-table>
  140. </el-tab-pane>
  141. <el-tab-pane label="功能定义" name="fun">
  142. <div class="wu-title">
  143. <div class="title">功能定义</div>
  144. <div>
  145. <el-button size="small" type="primary" v-auth="'add'" @click="onOpenEditFun()">添加</el-button>
  146. </div>
  147. </div>
  148. <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'fun'">
  149. <el-table-column label="功能标识" align="center" prop="key" />
  150. <el-table-column label="名称" prop="name" show-overflow-tooltip />
  151. <el-table-column label="描述" prop="desc" show-overflow-tooltip />
  152. <el-table-column label="操作" width="300" align="center" fixed="right">
  153. <template #default="scope">
  154. <el-button size="small" text type="warning" v-auth="'edit'" @click="onEditFun(scope.row)">修改</el-button>
  155. <el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'fun')">删除</el-button>
  156. </template>
  157. </el-table-column>
  158. </el-table>
  159. </el-tab-pane>
  160. <el-tab-pane label="事件定义" name="event">
  161. <div class="wu-title">
  162. <div class="title">事件定义</div>
  163. <div>
  164. <el-button type="primary" size="small" v-auth="'add'" @click="onOpenEditEvent()">添加</el-button>
  165. </div>
  166. </div>
  167. <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'event'">
  168. <el-table-column label="事件标识" align="center" prop="key" />
  169. <el-table-column label="名称" prop="name" show-overflow-tooltip />
  170. <el-table-column prop="level" label="事件级别" width="120" align="center">
  171. <template #default="scope">
  172. <el-tag type="primary" size="small" v-if="scope.row.level == 0">普通</el-tag>
  173. <el-tag type="warning" size="small" v-if="scope.row.level == 1">警告</el-tag>
  174. <el-tag type="danger" size="small" v-if="scope.row.level == 2">紧急</el-tag>
  175. </template>
  176. </el-table-column>
  177. <el-table-column label="描述" prop="desc" show-overflow-tooltip />
  178. <el-table-column label="操作" width="300" align="center" fixed="right">
  179. <template #default="scope">
  180. <el-button size="small" text type="warning" v-auth="'edit'" @click="onEditEvent(scope.row)">修改</el-button>
  181. <el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'event')">删除</el-button>
  182. </template>
  183. </el-table-column>
  184. </el-table>
  185. </el-tab-pane>
  186. <el-tab-pane label="标签定义" name="tab">
  187. <div class="wu-title">
  188. <div class="title">标签定义</div>
  189. <div>
  190. <el-button size="small" type="primary" v-auth="'add'" @click="onOpenEditTab()">添加</el-button>
  191. </div>
  192. </div>
  193. <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'tab'">
  194. <el-table-column label="属性标识" align="center" prop="key" />
  195. <el-table-column label="属性名称" prop="name" show-overflow-tooltip />
  196. <el-table-column prop="valueType" label="数据类型" width="120" align="center">
  197. <template #default="scope">
  198. <span>{{ scope.row.valueType.type }}</span>
  199. </template>
  200. </el-table-column>
  201. <el-table-column prop="accessMode" label="是否只读" width="120" align="center">
  202. <template #default="scope">
  203. <el-tag type="info" size="small" v-if="scope.row.accessMode">只读</el-tag>
  204. <el-tag type="success" size="small" v-else>读写</el-tag>
  205. </template>
  206. </el-table-column>
  207. <el-table-column label="描述" prop="desc" show-overflow-tooltip />
  208. <el-table-column label="操作" width="300" align="center" fixed="right">
  209. <template #default="scope">
  210. <el-button size="small" text type="warning" v-auth="'edit'" @click="onEditTag(scope.row)">修改</el-button>
  211. <el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'tab')">删除</el-button>
  212. </template>
  213. </el-table-column>
  214. </el-table>
  215. </el-tab-pane>
  216. </el-tabs>
  217. <pagination v-show="tableData.total > 0" :total="tableData.total" v-model:page="tableData.param.pageNum" v-model:limit="tableData.param.pageSize" @pagination="getList()" />
  218. </div>
  219. </el-tab-pane>
  220. <el-tab-pane label="设备功能" name="5">
  221. <functionCom :device-key="detail.key" :product-key="prodetail.key" v-if="detail.key && prodetail.key && activeName === '5'"></functionCom>
  222. </el-tab-pane>
  223. <el-tab-pane label="日志管理" name="4">
  224. <div class="system-user-search mb15">
  225. <el-form :model="logtableData.param" ref="logqueryRef" inline label-width="68px">
  226. <el-form-item label="日志类型" prop="types">
  227. <el-select v-model="logtableData.param.types" placeholder="日志类型" clearable>
  228. <el-option v-for="item in logTypeData" :key="item" :label="item" :value="item" />
  229. </el-select>
  230. </el-form-item>
  231. <el-form-item label="创建时间" prop="dateRange">
  232. <el-date-picker v-model="logtableData.param.dateRange" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
  233. </el-form-item>
  234. <el-form-item>
  235. <el-button type="primary" class="ml10" @click="getlog">
  236. <el-icon>
  237. <ele-Search />
  238. </el-icon>
  239. 查询
  240. </el-button>
  241. <el-button @click="resetQuery(logqueryRef)">
  242. <el-icon>
  243. <ele-Refresh />
  244. </el-icon>
  245. 重置
  246. </el-button>
  247. </el-form-item>
  248. </el-form>
  249. </div>
  250. <el-table style="width: 100%" :data="logtableData.data">
  251. <el-table-column label="类型" align="center" prop="type" />
  252. <el-table-column label="时间" prop="ts" show-overflow-tooltip />
  253. <el-table-column label="内容" prop="content" show-overflow-tooltip />
  254. <el-table-column label="操作" width="300" align="center" fixed="right">
  255. <template #default="scope">
  256. <el-button size="small" text type="warning" @click="onLogDetail(scope.row)">查看</el-button>
  257. </template>
  258. </el-table-column>
  259. </el-table>
  260. <pagination v-show="logtableData.total > 0" :total="logtableData.total" v-model:page="logtableData.param.pageNum" v-model:limit="logtableData.param.pageSize" @pagination="getlog" />
  261. </el-tab-pane>
  262. <el-tab-pane v-if="prodetail.deviceType == '网关'" label="子设备" name="6">
  263. <div class="wu-box">
  264. <div class="wu-title">
  265. <div class="title">子设备列表</div>
  266. <div>
  267. <el-button v-auth="'mutipleBind'" type="primary" @click="onOpenMutipleBind()">批量绑定</el-button>
  268. <el-button v-auth="'cancleMutipleBind'" :disabled="!deviceKeyList.length" type="primary" @click="mutipleUnbind()">批量解绑</el-button>
  269. </div>
  270. </div>
  271. <el-table :data="deviceTableData.data" style="width: 100%" @selection-change="handleSelectionChange" v-loading="deviceTableData.loading">
  272. <el-table-column type="selection" width="55" align="center" />
  273. <el-table-column label="标识" prop="key" width="130" show-overflow-tooltip />
  274. <el-table-column label="设备名称" prop="name" show-overflow-tooltip />
  275. <el-table-column label="产品名称" prop="productName" show-overflow-tooltip />
  276. <el-table-column prop="status" label="状态" width="100" align="center">
  277. <template #default="scope">
  278. <el-tag type="info" size="small" v-if="scope.row.status == 1">离线</el-tag>
  279. <el-tag type="success" size="small" v-if="scope.row.status == 2">在线</el-tag>
  280. <el-tag type="info" size="small" v-if="scope.row.status == 0">未启用</el-tag>
  281. </template>
  282. </el-table-column>
  283. <el-table-column prop="registryTime" label="激活时间" align="center" width="150"></el-table-column>
  284. <el-table-column prop="desc" label="说明"></el-table-column>
  285. <el-table-column label="操作" width="160" align="center" fixed="right">
  286. <template #default="scope">
  287. <el-button size="small" text type="danger" v-auth="'del'" @click="deleteSubDevice(scope.row)">删除</el-button>
  288. <el-button size="small" text type="warning" v-auth="'detail'" @click="onOpenDetail(scope.row)">详情</el-button>
  289. </template>
  290. </el-table-column>
  291. </el-table>
  292. <pagination v-show="deviceTableData.total > 0" :total="deviceTableData.total" v-model:page="deviceTableData.param.pageNum" v-model:limit="deviceTableData.param.pageSize" @pagination="getDeviceTableData" />
  293. </div>
  294. </el-tab-pane>
  295. <el-tab-pane label="设备扩展属性信息" name="7">
  296. <el-form label-width="110px">
  297. <el-form-item label="设备图片">
  298. <el-image class="mr20" style="border: 1px solid #e5e5e5;border-radius: 8px;width: 100px;height: 100px;object-fit: contain;" :src="item" v-for="(item, index) in phone" :key="index" />
  299. </el-form-item>
  300. <el-form-item label="证书图片">
  301. <el-image class="mr20" style="border: 1px solid #e5e5e5;border-radius: 8px;width: 100px;height: 100px;object-fit: contain;" :src="item" v-for="(item, index) in certificate" :key="index" />
  302. </el-form-item>
  303. <el-form-item label="设备说明">
  304. <el-input disabled v-model="intro" type="textarea" placeholder="请输入设备说明"></el-input>
  305. </el-form-item>
  306. </el-form>
  307. </el-tab-pane>
  308. </el-tabs>
  309. </div>
  310. <EditDic ref="editDicRef" @typeList="initData" />
  311. <EditAttr ref="editAttrRef" @typeList="getproperty" />
  312. <EditFun ref="editFunRef" @typeList="getfunction" />
  313. <EditEvent ref="editEventRef" @typeList="getevent" />
  314. <EditTab ref="editTabRef" @typeList="gettab" />
  315. <ListDic ref="listDicRef" />
  316. <SubDevice ref="subDeviceRef" />
  317. <setAttr :device-key="detail.key" ref="setAttrRef" />
  318. <!-- 子设备-批量绑定弹窗 -->
  319. <SubDeviceMutipleBind ref="mutipleBindRef" @bindSuccess="getDeviceTableData" />
  320. <el-dialog v-model="dialogVisible" title="日志数据内容" width="30%">
  321. <JsonViewer :value="jsonData" boxed sort theme="jv-dark" @click="onKeyclick" />
  322. <template #footer>
  323. <span class="dialog-footer">
  324. <el-button @click="dialogVisible = false">关闭</el-button>
  325. </span>
  326. </template>
  327. </el-dialog>
  328. </div>
  329. </template>
  330. <script lang="ts">
  331. import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
  332. import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
  333. import functionCom from './component/function.vue';
  334. import 'vue3-json-viewer/dist/index.css';
  335. import EditDic from './component/edit.vue';
  336. // import EditDic from '../product/component/editPro.vue';
  337. import EditAttr from '../product/component/editAttr.vue';
  338. import EditFun from '../product/component/editFun.vue';
  339. import EditEvent from '../product/component/editEvent.vue';
  340. import EditTab from '../product/component/editTab.vue';
  341. import devantd from '/@/components/devantd/index.vue';
  342. import ListDic from './component/list.vue';
  343. import SubDevice from './component/subDevice.vue';
  344. import setAttr from './component/setAttr.vue';
  345. import SubDeviceMutipleBind from './component/subDeviceMutipleBind.vue';
  346. import api from '/@/api/device';
  347. import datahub from '/@/api/datahub';
  348. import { useRoute } from 'vue-router';
  349. interface TableDataState {
  350. phone: any[];
  351. certificate: any[];
  352. intro: string;
  353. ids: number[];
  354. detail: any;
  355. deviceKeyList: string[];
  356. deviceTableData: {
  357. data: [];
  358. total: number;
  359. loading: boolean;
  360. param: {
  361. pageNum: number;
  362. pageSize: number;
  363. gatewayKey: string;
  364. dateRange: string[];
  365. };
  366. };
  367. tableData: {
  368. data: [];
  369. total: number;
  370. loading: boolean;
  371. param: {
  372. pageNum: number;
  373. pageSize: number;
  374. name: string;
  375. deviceType: string;
  376. status: string;
  377. dateRange: string[];
  378. };
  379. };
  380. logtableData: {
  381. data: [];
  382. total: number;
  383. loading: boolean;
  384. param: {
  385. pageNum: number;
  386. pageSize: number;
  387. name: string;
  388. deviceType: string;
  389. status: string;
  390. dateRange: string[];
  391. };
  392. };
  393. }
  394. export default defineComponent({
  395. name: 'deviceEditPro',
  396. components: { SubDeviceMutipleBind, SubDevice, EditDic, EditAttr, EditFun, EditEvent, EditTab, devantd, ListDic, functionCom, setAttr },
  397. setup(prop, context) {
  398. const logqueryRef = ref();
  399. const array_list = ref([]);
  400. const route = useRoute();
  401. const editDicRef = ref();
  402. const setAttrRef = ref();
  403. const editAttrRef = ref();
  404. const editFunRef = ref();
  405. const listDicRef = ref();
  406. const editEventRef = ref();
  407. const editTabRef = ref();
  408. const subDeviceRef = ref();
  409. const mutipleBindRef = ref();
  410. const state = reactive<TableDataState>({
  411. certificate: [],
  412. phone: [],
  413. intro: '',
  414. deviceKeyList: [],
  415. areaData: [],
  416. isShowDialog: false,
  417. dialogVisible: false,
  418. logTypeData: [],
  419. jsonData: '',
  420. activeName: '3', // 分类数据
  421. activetab: 'attr', // 分类数据
  422. detail: {},
  423. prodetail: [],
  424. product_id: 0,
  425. developer_status: 0,
  426. deviceTableData: {
  427. data: [],
  428. total: 0,
  429. loading: false,
  430. param: {
  431. pageNum: 1,
  432. gatewayKey: '',
  433. pageSize: 10,
  434. dateRange: [],
  435. },
  436. },
  437. tableData: {
  438. data: [],
  439. total: 0,
  440. loading: false,
  441. param: {
  442. pageNum: 1,
  443. productId: 0,
  444. pageSize: 10,
  445. status: '',
  446. dateRange: [],
  447. },
  448. },
  449. logtableData: {
  450. data: [],
  451. total: 0,
  452. loading: false,
  453. param: {
  454. pageNum: 1,
  455. productId: 0,
  456. pageSize: 10,
  457. status: '',
  458. dateRange: [],
  459. },
  460. },
  461. });
  462. onMounted(() => {
  463. initData()
  464. });
  465. function initData() {
  466. const ids = route.params && route.params.id;
  467. api.instance.detail(ids).then((res: any) => {
  468. state.detail = res.data;
  469. state.developer_status = res.data.status;
  470. state.tableData.param.productId = res.data.product.id;
  471. state.product_id = res.data.product.id;
  472. api.product.detail(res.data.product.id).then((res: any) => {
  473. state.prodetail = res.data;
  474. });
  475. const { phone, certificate, intro } = JSON.parse(res.data.extensionInfo || '{}')
  476. state.phone = phone || [];
  477. state.certificate = certificate || [];
  478. state.intro = intro
  479. //加载全部属性
  480. datahub.node.getpropertyList({ key: state.detail.product.key }).then((re: any) => {
  481. array_list.value = re;
  482. });
  483. //第一次加载
  484. api.model.property(state.tableData.param).then((res: any) => {
  485. state.tableData.data = res.Data;
  486. state.tableData.total = res.Total;
  487. });
  488. getrunData();
  489. getDeviceTableData()
  490. });
  491. }
  492. const mutipleUnbind = () => {
  493. let msg = '是否进行批量解绑?';
  494. if (state.deviceKeyList.length === 0) {
  495. ElMessage.error('请选择要批量解绑的数据。');
  496. return;
  497. }
  498. ElMessageBox.confirm(msg, '提示', {
  499. confirmButtonText: '确认',
  500. cancelButtonText: '取消',
  501. type: 'warning',
  502. })
  503. .then(() => {
  504. api.device.mutipleUnbind({
  505. "gatewayKey": state.deviceTableData.param.gatewayKey,
  506. "subKeys": state.deviceKeyList
  507. }).then(() => {
  508. ElMessage.success('解绑成功');
  509. // typeList();
  510. getDeviceTableData();
  511. });
  512. })
  513. .catch(() => { });
  514. }
  515. const getDeviceTableData = () => {
  516. state.deviceTableData.param.gatewayKey = state.detail.key;
  517. api.device.getList(state.deviceTableData.param).then((res: any) => {
  518. state.deviceTableData.data = res.list;
  519. state.deviceTableData.total = res.Total;
  520. });
  521. };
  522. // 多选框选中数据
  523. const handleSelectionChange = (selection: any[]) => {
  524. state.deviceKeyList = selection.map((item) => item.key);
  525. };
  526. // 打开修改产品弹窗
  527. const onOpenDetail = (row: any) => {
  528. subDeviceRef.value.openDialog(row)
  529. };
  530. // 删除子设备
  531. const deleteSubDevice = (row: any) => {
  532. ElMessageBox.confirm(`此操作将永久删除分类:${row.name}, 是否继续?`, '提示', {
  533. confirmButtonText: '删除',
  534. cancelButtonText: '取消',
  535. type: 'warning',
  536. }).then(() => {
  537. api.product.deleteSubDevice(row.id).then(() => {
  538. ElMessage.success('删除成功');
  539. getDeviceTableData();
  540. });
  541. });
  542. };
  543. const onLogDetail = (row: TableDataRow) => {
  544. state.jsonData = JSON.parse(row.content);
  545. state.dialogVisible = true;
  546. };
  547. const onOpenMutipleBind = () => {
  548. mutipleBindRef.value.openDialog(state.deviceTableData.param.gatewayKey);
  549. };
  550. //编辑属性
  551. const onEditAttr = (row: TableDataRow) => {
  552. editAttrRef.value.openDialog(row, state.product_id);
  553. };
  554. //编辑功能
  555. const onEditFun = (row: TableDataRow) => {
  556. editFunRef.value.openDialog(row, state.product_id);
  557. };
  558. //编辑事件
  559. const onEditEvent = (row: TableDataRow) => {
  560. editEventRef.value.openDialog(row, state.product_id);
  561. };
  562. //编辑标签
  563. const onEditTag = (row: TableDataRow) => {
  564. editTabRef.value.openDialog(row, state.product_id);
  565. };
  566. //打开添加属性弹窗
  567. const onOpenEditAttr = () => {
  568. editAttrRef.value.openDialog({ product_id: state.product_id, id: 0, accessMode: 0 });
  569. };
  570. //打开添加功能弹窗
  571. const onOpenEditFun = () => {
  572. editFunRef.value.openDialog({ product_id: state.product_id, id: 0 });
  573. };
  574. //打开添加事件弹窗
  575. const onOpenEditEvent = () => {
  576. editEventRef.value.openDialog({ product_id: state.product_id, id: 0, level: 0 });
  577. };
  578. //打开添加事件弹窗
  579. const onOpenEditTab = () => {
  580. editTabRef.value.openDialog({ product_id: state.product_id, id: 0, accessMode: 0 });
  581. };
  582. //查看日志列表
  583. const onOpenListDetail = (row: TableDataRow) => {
  584. listDicRef.value.openDialog(row, state.detail.key);
  585. };
  586. // 打开修改产品弹窗
  587. const onOpenEditDic = (row: TableDataRow) => {
  588. editDicRef.value.openDialog(row);
  589. };
  590. // 删除产品
  591. const onRowDel = (key, type) => {
  592. let msg = `此操作将永久删除该数据,是否继续?`;
  593. if (key.length === 0) {
  594. ElMessage.error('请选择要删除的数据。');
  595. return;
  596. }
  597. ElMessageBox.confirm(msg, '提示', {
  598. confirmButtonText: '确认',
  599. cancelButtonText: '取消',
  600. type: 'warning',
  601. })
  602. .then(() => {
  603. if (type == 'attr') {
  604. api.model.propertydel(state.product_id, key).then(() => {
  605. ElMessage.success('删除成功');
  606. getproperty();
  607. });
  608. }
  609. if (type == 'fun') {
  610. api.model.functiondel(state.product_id, key).then(() => {
  611. ElMessage.success('删除成功');
  612. getfunction();
  613. });
  614. }
  615. if (type == 'event') {
  616. api.model.eventdel(state.product_id, key).then(() => {
  617. ElMessage.success('删除成功');
  618. getevent();
  619. });
  620. }
  621. if (type == 'tab') {
  622. api.model.tagdel(state.product_id, key).then(() => {
  623. ElMessage.success('删除成功');
  624. tagdel();
  625. });
  626. }
  627. })
  628. .catch(() => { });
  629. };
  630. //根据不同类型获取列表
  631. const getList = () => {
  632. switch (state.activetab) {
  633. case 'attr':
  634. getproperty();
  635. break;
  636. case 'fun':
  637. getfunction();
  638. break;
  639. case 'event':
  640. getevent();
  641. break;
  642. case 'tab':
  643. gettab();
  644. break;
  645. }
  646. };
  647. const getproperty = () => {
  648. api.model.property(state.tableData.param).then((res: any) => {
  649. state.tableData.data = res.Data;
  650. state.tableData.total = res.Total;
  651. });
  652. };
  653. const getfunction = () => {
  654. api.model.function(state.tableData.param).then((res: any) => {
  655. state.tableData.data = res.Data;
  656. state.tableData.total = res.Total;
  657. });
  658. };
  659. const getevent = () => {
  660. api.model.event(state.tableData.param).then((res: any) => {
  661. state.tableData.data = res.Data;
  662. state.tableData.total = res.Total;
  663. });
  664. };
  665. const gettab = () => {
  666. api.model.tag(state.tableData.param).then((res: any) => {
  667. state.tableData.data = res.Data;
  668. state.tableData.total = res.Total;
  669. });
  670. };
  671. const wuhandleClick = (tab: TabsPaneContext) => {
  672. state.activetab = tab.props.name;
  673. switch (tab.props.name) {
  674. case 'attr':
  675. getproperty();
  676. break;
  677. case 'fun':
  678. getfunction();
  679. break;
  680. case 'event':
  681. getevent();
  682. break;
  683. case 'tab':
  684. gettab();
  685. break;
  686. }
  687. };
  688. const handleClick = (tab: TabsPaneContext, event: Event) => {
  689. if (tab.props.name == 4) {
  690. //获取日志
  691. getlog();
  692. getlogtype();
  693. } else if (tab.props.name == 2) {
  694. getList();
  695. } else if (tab.props.name == 3) {
  696. getrunData();
  697. }
  698. };
  699. const getValueText = (key, value) => {
  700. let data = array_list.value;
  701. for (let i = 0; i < data.length; i++) {
  702. const item = data[i];
  703. if (item.key === key) {
  704. if (item.valueType.type === "enum") {
  705. const option = item.valueType.elements.find((element) => element.value === value);
  706. if (option) {
  707. return option.text;
  708. }
  709. } else {
  710. return value;
  711. }
  712. }
  713. }
  714. return value;
  715. }
  716. const getStatusText = (name, value) => {
  717. let data = array_list.value;
  718. for (let i = 0; i < data.length; i++) {
  719. const field = data[i];
  720. if (field.valueType.type === "object") {
  721. for (let j = 0; j < field.valueType.properties.length; j++) {
  722. const property = field.valueType.properties[j];
  723. if (property.key === name) {
  724. if (property.valueType.type === "enum") {
  725. const element = property.valueType.elements.find((element) => element.value === value);
  726. if (element) {
  727. return `${property.name}: ${element.text}`;
  728. } else {
  729. return `${property.name}: ${value}`;
  730. }
  731. } else {
  732. return `${property.name}: ${value}`;
  733. }
  734. }
  735. }
  736. } else if (field.key === name) {
  737. if (field.valueType.type === "enum") {
  738. const element = field.valueType.elements.find((element) => element.value === value);
  739. if (element) {
  740. return `${field.name}: ${element.text}`;
  741. }
  742. } else {
  743. return `${field.name}: ${value}`;
  744. }
  745. }
  746. }
  747. return name + ':' + value;
  748. }
  749. const getrunData = () => {
  750. api.instance.getrun_status({ deviceKey: state.detail.key }).then((res: any) => {
  751. state.areaData = res
  752. let properties = state.areaData.properties || [];
  753. var temp = new Array();
  754. properties.forEach(function (item, index) {
  755. let datalist = item.list || [];
  756. temp[index] = [];
  757. var temps = new Array();
  758. datalist.forEach(function (a, b) {
  759. if (b < 15) {
  760. temps.push(a);
  761. }
  762. });
  763. if (item.type == 'object') {
  764. item.value = JSON.parse(item.value);
  765. }
  766. temp[index]['name'] = item.name
  767. temp[index]['key'] = item.key
  768. temp[index]['type'] = item.type
  769. temp[index]['unit'] = item.unit
  770. temp[index]['value'] = item.value
  771. temp[index]['list'] = temps
  772. });
  773. state.areaData.properties = temp;
  774. });
  775. };
  776. const getlogtype = () => {
  777. api.instance.getlogcate({}).then((res: any) => {
  778. state.logTypeData = res.list;
  779. });
  780. };
  781. const getlog = () => {
  782. state.logtableData.param.deviceKey = state.detail.key;
  783. api.instance.getLogList(state.logtableData.param).then((res: any) => {
  784. state.logtableData.data = res.list;
  785. state.logtableData.total = res.Total;
  786. });
  787. };
  788. /** 重置按钮操作 */
  789. const resetQuery = (formEl: FormInstance | undefined) => {
  790. if (!formEl) return;
  791. formEl.resetFields();
  792. getlog();
  793. };
  794. const CkOption = () => {
  795. if (state.developer_status == 2) {
  796. api.instance.devoffline({ id: state.detail.id }).then((res: any) => {
  797. ElMessage.success('操作成功');
  798. state.developer_status = 1;
  799. });
  800. } else {
  801. api.instance.devonline({ id: state.detail.id }).then((res: any) => {
  802. ElMessage.success('操作成功');
  803. state.developer_status = 2;
  804. });
  805. }
  806. };
  807. const tinyAreas = () => {
  808. var data = state.data;
  809. const tinyArea = new TinyArea('container', {
  810. height: 60,
  811. autoFit: false,
  812. data,
  813. smooth: true,
  814. areaStyle: {
  815. fill: '#d6e3fd',
  816. },
  817. });
  818. tinyArea.render();
  819. }
  820. const onlineTimeoutUpdate = () => {
  821. if (!state.detail.onlineTimeout) return ElMessage('请先输入设备超时时间')
  822. api.device.updateOnlineTimeout({ id: state.detail.id, onlineTimeout: state.detail.onlineTimeout }).then(() => {
  823. ElMessage.success('设置成功')
  824. })
  825. }
  826. const setAttr = (row: any) => {
  827. setAttrRef.value.show(row)
  828. }
  829. return {
  830. initData,
  831. logqueryRef,
  832. resetQuery,
  833. getStatusText,
  834. getValueText,
  835. onlineTimeoutUpdate,
  836. setAttr,
  837. tinyAreas,
  838. setAttrRef,
  839. editDicRef,
  840. editAttrRef,
  841. listDicRef,
  842. editFunRef,
  843. editEventRef,
  844. editTabRef,
  845. subDeviceRef,
  846. mutipleBindRef,
  847. onOpenListDetail,
  848. getrunData,
  849. getlog,
  850. getlogtype,
  851. onLogDetail,
  852. CkOption,
  853. onRowDel,
  854. onEditFun,
  855. onEditEvent,
  856. onEditTag,
  857. onEditAttr,
  858. getList,
  859. getproperty,
  860. getDeviceTableData,
  861. handleSelectionChange,
  862. getfunction,
  863. getevent,
  864. gettab,
  865. wuhandleClick,
  866. onOpenEditTab,
  867. onOpenEditEvent,
  868. onOpenEditAttr,
  869. onOpenEditFun,
  870. onOpenEditDic,
  871. onOpenDetail,
  872. deleteSubDevice,
  873. handleClick,
  874. onOpenMutipleBind,
  875. mutipleUnbind,
  876. ...toRefs(state),
  877. };
  878. },
  879. });
  880. </script>
  881. <style scoped>
  882. .oblist {
  883. display: flex;
  884. flex-direction: row;
  885. align-items: center;
  886. padding-right: 5px;
  887. flex: 1;
  888. margin-top: 10px;
  889. margin-left: 10px;
  890. }
  891. .content {
  892. width: 100%;
  893. padding: 20px 20px;
  894. }
  895. .content-box {
  896. width: 100%;
  897. padding: 0 20px;
  898. }
  899. .cont_box {
  900. display: flex;
  901. }
  902. .cont_box .title {
  903. font-size: 24px;
  904. }
  905. .cont_box .pro-status {
  906. line-height: 40px;
  907. margin-left: 30px;
  908. }
  909. .cont_box .pro-status .on {
  910. background: #52c41a;
  911. }
  912. .cont_box .pro-status .off {
  913. background: #c41a1a;
  914. }
  915. .cont_box .pro-status span {
  916. position: relative;
  917. top: -1px;
  918. display: inline-block;
  919. width: 6px;
  920. height: 6px;
  921. vertical-align: middle;
  922. border-radius: 50%;
  923. margin-right: 5px;
  924. }
  925. .cont_box .pro-option {
  926. line-height: 40px;
  927. margin-left: 10px;
  928. color: #1890ff;
  929. cursor: pointer;
  930. }
  931. .content-box .pro-box {
  932. display: flex;
  933. padding: 10px;
  934. justify-content: space-between;
  935. }
  936. .content-box .pro-box .protitle {
  937. font-size: 18px;
  938. font-weight: bold;
  939. line-height: 35px;
  940. }
  941. .content-box .pro-box .buttonedit {
  942. border: 0px;
  943. color: #1890ff;
  944. }
  945. table {
  946. border-collapse: collapse;
  947. text-indent: initial;
  948. border-spacing: 2px;
  949. }
  950. tbody {
  951. box-sizing: border-box;
  952. display: table-row-group;
  953. vertical-align: middle;
  954. border-color: inherit;
  955. }
  956. tr {
  957. display: table-row;
  958. vertical-align: inherit;
  959. border-color: inherit;
  960. }
  961. .wu-box {
  962. border: #e8e8e8 solid 1px;
  963. padding: 20px;
  964. width: 100%;
  965. }
  966. .wu-box .wu-title {
  967. display: flex;
  968. flex-direction: row;
  969. justify-content: space-between;
  970. padding: 20px;
  971. border-bottom: #e8e8e8 1px solid;
  972. }
  973. .wu-box .wu-title .title {
  974. font-size: 18px;
  975. }
  976. .ant-card {
  977. box-sizing: border-box;
  978. margin: 10px;
  979. width: 23.2%;
  980. font-size: 14px;
  981. font-variant: tabular-nums;
  982. border: 1px solid var(--next-border-color-light);
  983. line-height: 1.5;
  984. list-style: none;
  985. font-feature-settings: 'tnum';
  986. position: relative;
  987. border-radius: 2px;
  988. transition: all 0.3s;
  989. }
  990. .ant-card-body {
  991. padding: 24px;
  992. zoom: 1;
  993. }
  994. .cardflex {
  995. display: flex;
  996. justify-content: space-between;
  997. }
  998. .statusname {
  999. font-size: 30px;
  1000. margin-top: 10px;
  1001. margin-bottom: 15px;
  1002. }
  1003. .comtest {
  1004. margin-top: 20px;
  1005. height: 30px;
  1006. line-height: 30px;
  1007. }
  1008. </style>