detail.vue 37 KB

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