detail.vue 42 KB

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