detail.vue 40 KB

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