deviceDetails.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. <template>
  2. <oa-scroll
  3. customClass="deviceDetails-container scroll-height"
  4. :isSticky="false"
  5. :refresherLoad="false"
  6. :refresherEnabled="false"
  7. :refresherEnabledTitle="false"
  8. :refresherDefaultStyle="'none'"
  9. :refresherThreshold="44"
  10. :refresherBackground="'#f5f6f7'"
  11. :data-theme="'theme-' + proxy.$settingStore.themeColor.name"
  12. >
  13. <template #default>
  14. <view class="header-area p15">
  15. <view class="header-area-top flex mb15">
  16. <view class="title"> {{ commonStore.deviceManageData.deviceName }} </view>
  17. <view class="status" :style="{ backgroundColor: commonStore.deviceManageData.deviceStatus == 1 ? '#16bf00' : 'red' }">
  18. {{ commonStore.deviceManageData.deviceStatus == 1 ? "在线" : "离线" }}
  19. </view>
  20. </view>
  21. <u-row class="header-area-center p0">
  22. <u-col span="9">
  23. <view class="header-area-center-item" v-for="data in listData" :key="data">
  24. <span class="title">{{ data.title }}:</span>
  25. <span class="value">{{ commonStore.deviceManageData[data.prop] ? commonStore.deviceManageData[data.prop] : "-" }}</span>
  26. </view>
  27. </u-col>
  28. <u-col span="3">
  29. <image style="width: 80px; height: 80px; display: flex; margin: auto 0 auto auto" :src="commonStore.deviceManageData.typeImg" mode="aspectFill"></image>
  30. </u-col>
  31. </u-row>
  32. </view>
  33. <view class="body-area">
  34. <!-- 分段器组件 -->
  35. <view class="subsection">
  36. <view class="subsection-item" v-for="(li, index) in tabs.list" :key="index" :class="{ active: index == tabs.value }" @click="tabPositionChange(index)">{{ li }}</view>
  37. </view>
  38. <view class="realTimeData-area flex plr15" v-if="tabs.value == 0">
  39. <view class="realTimeData-area-item" v-for="item in realTimeData" :key="item">
  40. <view class="title">{{ item.attributeName }}</view>
  41. <view class="value">
  42. <span v-if="item.attributeDict.length > 0">
  43. {{ Number.isFinite(item.value) ? proxy.$common.mapping("name", "value", item.value, item.attributeDict) : item.value ? item.value : "-" }}
  44. </span>
  45. <span v-else>
  46. {{ Number.isFinite(item.value) ? (Number.isInteger(item.value) ? item.value : item.value.toFixed(2)) : item.value ? item.value : "-" }}
  47. </span>
  48. <span style="color: #333; width: auto; font-size: 14px">{{ item.attributeUnit }} </span>
  49. </view>
  50. </view>
  51. </view>
  52. <view class="plr15" v-if="tabs.value == 1">
  53. <view class="flex" :style="{ color: proxy.$settingStore.themeColor.color }">
  54. <view class="ml10" style="margin-left: auto" @click="open">选择时间</view>
  55. <view class="ml10" @click="modalShow = true">筛选</view>
  56. </view>
  57. <chart :currentDateList="ecahrtsDate"></chart>
  58. </view>
  59. <view class="cotrol-area plr15" v-if="tabs.value == 2">
  60. <view class="cotrol-area-item p10 mb15" v-for="(item, index) in deviceCotrolData" :key="index">
  61. <view class="title flex" v-if="item.commandDict.length > 0 && item.commandName.indexOf('开关') != -1">
  62. <span style="margin: auto 0">{{ item.commandName }}</span>
  63. <span style="margin: auto 0 auto auto">
  64. <u-switch v-model="item.commandValue" :activeValue="1" :inactiveValue="0" size="20" @change="selectItem(item)"></u-switch>
  65. </span>
  66. </view>
  67. <u-select
  68. v-else-if="item.commandDict.length > 0 && item.commandName.indexOf('开关') == -1"
  69. v-model:current="item.commandValue"
  70. :label="item.commandName"
  71. :options="item.commandDict"
  72. keyName="value"
  73. labelName="name"
  74. @select="selectItem(item)"
  75. >
  76. <template #text>
  77. <view class="title">
  78. <span>{{ item.commandName }}</span>
  79. <span v-if="item.commandValue">({{ proxy.$common.mapping("name", "value", item.commandValue, item.commandDict) }})</span>
  80. </view>
  81. </template>
  82. </u-select>
  83. <view class="title" v-else>
  84. <view class="flex mb10">
  85. <span style="margin: auto 0"> {{ item.commandName }}</span>
  86. <span style="margin: auto 0 auto auto"> {{ item.commandValue }}</span>
  87. </view>
  88. <u-slider v-model="item.commandValue" :min="item.minimum" :max="item.maximum" :step="item.dataType == 4 ? 0.1 : 1" height="5px" @change="selectItem(item)"></u-slider>
  89. </view>
  90. </view>
  91. </view>
  92. </view>
  93. <u-modal :show="modalShow" @confirm="modalShow = false" @close="modalShow = false" :closeOnClickOverlay="true">
  94. <view class="slot-content">
  95. <u-checkbox-group
  96. v-model="checkbox.value"
  97. @change="
  98. (val) => {
  99. checkboxChange(val);
  100. }
  101. "
  102. :size="14"
  103. :activeColor="proxy.$settingStore.themeColor.color"
  104. >
  105. <u-checkbox class="mb10" v-for="option in checkbox.list" :key="option" :label="option.attributeName" :name="option.attributeCode"> </u-checkbox>
  106. </u-checkbox-group>
  107. </view>
  108. </u-modal>
  109. <uni-calendar ref="calendar" class="uni-calendar--hook" :clearDate="false" :insert="false" :lunar="false" :range="true" @confirm="calendarConfirm" />
  110. </template>
  111. </oa-scroll>
  112. </template>
  113. <script setup>
  114. /*----------------------------------依赖引入-----------------------------------*/
  115. import { onLoad, onShow, onReady, onHide, onLaunch, onNavigationBarButtonTap, onPageScroll } from "@dcloudio/uni-app";
  116. import { ref, reactive, computed, getCurrentInstance, toRefs, inject, watch } from "vue";
  117. /*----------------------------------接口引入-----------------------------------*/
  118. import { dmpProductAttribute, historyMetrics, last, getList, control } from "@/api/business/fireIot/deviceManage.js";
  119. /*----------------------------------组件引入-----------------------------------*/
  120. import chart from "./chart.vue";
  121. /*----------------------------------store引入-----------------------------------*/
  122. import { useStores, commonStores } from "@/store/modules/index";
  123. /*----------------------------------公共方法引入-----------------------------------*/
  124. /*----------------------------------公共变量-----------------------------------*/
  125. const { proxy } = getCurrentInstance();
  126. const commonStore = commonStores();
  127. /*----------------------------------变量声明-----------------------------------*/
  128. const state = reactive({
  129. listData: [
  130. { title: "设备类型", prop: "productName" },
  131. { title: "设备编号", prop: "deviceId" },
  132. { title: "SIM卡号", prop: "simCode" },
  133. { title: "安装位置", prop: "installAddress" },
  134. { title: "添加时间", prop: "createdTime" },
  135. ],
  136. tabs: {
  137. list: ["实时数据", "历史数据", "指令操作"],
  138. value: 0,
  139. },
  140. metrics: [], //属性编号数据存储
  141. metricsValue: {}, //属性编号值数据存储
  142. realTimeData: [], //实时数据存储
  143. ecahrtsDate: [], //图表数据存储
  144. deviceCotrolData: [], //设备调试数据存储
  145. checkbox: {
  146. list: [], //复选框渲染数据存储
  147. value: [], //复选框值数据存储
  148. },
  149. });
  150. const { listData, tabs, metrics, metricsValue, realTimeData, ecahrtsDate, deviceCotrolData, checkbox } = toRefs(state);
  151. const modalShow = ref(false); //模态框显示隐藏
  152. const calendar = ref(null);
  153. const calendarStartTime = ref(proxy.$dayjs().format("YYYY-MM-DD")); //日历开始时间
  154. const calendarEndTime = ref(proxy.$dayjs().format("YYYY-MM-DD")); //日历结束时间
  155. const productId = ref(0); //产品id
  156. const deviceId = ref(0); //设备id
  157. /**
  158. * @初始化
  159. */
  160. function init() {
  161. state.metrics = [];
  162. state.realTimeData = [];
  163. state.checkbox.list = [];
  164. dmpProductAttribute({
  165. current: 1,
  166. size: 100,
  167. attributeName: "",
  168. productId: productId.value,
  169. deviceId: deviceId.value,
  170. }).then((requset) => {
  171. if (requset.status === "SUCCESS") {
  172. requset.data.records.forEach((item) => {
  173. item.attributeDict = item.attributeDict ? JSON.parse(item.attributeDict) : [];
  174. state.metrics.push(item.attributeCode.toLowerCase());
  175. state.realTimeData.push(item);
  176. state.checkbox.list.push({
  177. ...item,
  178. attributeCode: item.attributeCode.toLowerCase(),
  179. });
  180. });
  181. last({
  182. metrics: state.metrics,
  183. deviceuuid: [commonStore.deviceManageData.deviceUuid],
  184. }).then((requset) => {
  185. if (requset.status != "SUCCESS") return;
  186. if (requset.data.length <= 0) return;
  187. state.metricsValue = requset.data[0].metrics;
  188. Object.keys(state.metricsValue).forEach((key) => {
  189. state.realTimeData.forEach((el) => {
  190. if (el.attributeCode.toLowerCase() === key) {
  191. el.value = state.metricsValue[key];
  192. }
  193. });
  194. });
  195. });
  196. }
  197. });
  198. }
  199. function deviceControlData() {
  200. state.deviceCotrolData = [];
  201. getList({
  202. current: 1,
  203. size: 10,
  204. productCode: commonStore.deviceManageData.productCode,
  205. }).then((response) => {
  206. response.data.records.forEach((e) => {
  207. e.commandDict = e.commandDict ? JSON.parse(e.commandDict) : [];
  208. e.commandValue = state.metricsValue[e.commandCode.toLowerCase()];
  209. state.deviceCotrolData.push(e);
  210. });
  211. });
  212. }
  213. function selectItem(e) {
  214. proxy.$modal.loading("加载中");
  215. var params = {
  216. commandCode: e.commandCode,
  217. commandValue: e.commandValue,
  218. productCode: e.productCode,
  219. deviceUuid: commonStore.deviceManageData.deviceUuid,
  220. categoryType: commonStore.deviceManageData.categoryType,
  221. gatewayUuid: commonStore.deviceManageData.gatewayUuid,
  222. };
  223. control(params).then((res) => {
  224. init();
  225. proxy.$modal.closeLoading();
  226. proxy.$modal.msg(res.data.message);
  227. });
  228. }
  229. /**
  230. * @tabs切换change事件
  231. */
  232. function tabPositionChange(index) {
  233. state.tabs.value = index;
  234. }
  235. /**
  236. * @checkbox选中change事件
  237. */
  238. function checkboxChange(value) {
  239. state.checkbox.value = value;
  240. historyMetricsApi();
  241. }
  242. /**
  243. * @日历确认事件
  244. */
  245. function calendarConfirm(e) {
  246. calendarStartTime.value = e.range.before;
  247. calendarEndTime.value = e.range.after ? e.range.after : e.range.before;
  248. historyMetricsApi();
  249. }
  250. /**
  251. * @设备多属性历史数据请求
  252. * @api接口请求
  253. */
  254. function historyMetricsApi() {
  255. historyMetrics({
  256. startTime: calendarStartTime.value ? calendarStartTime.value + " 00:00:00" : calendarStartTime.value,
  257. endTime: calendarEndTime.value ? calendarEndTime.value + " 23:59:59" : calendarEndTime.value,
  258. deviceuuid: [commonStore.deviceManageData.deviceUuid],
  259. metrics: state.checkbox.value.length > 0 ? state.checkbox.value : state.metrics,
  260. }).then((requset) => {
  261. if (requset.status != "SUCCESS") return;
  262. if (requset.data.length <= 0) return;
  263. var metrics = requset.data[0].metrics;
  264. state.checkbox.list.forEach((el) => {
  265. metrics.forEach((e) => {
  266. if (el.attributeCode.toLowerCase() == e.metric) {
  267. e.attributeName = el.attributeName;
  268. }
  269. });
  270. });
  271. metrics.forEach((el) => {
  272. el.data = [];
  273. if (el.metricItems.length > 0) {
  274. el.metricItems.forEach((e) => {
  275. el.data.push([e.timestamp, e.value]);
  276. });
  277. }
  278. });
  279. state.ecahrtsDate = metrics;
  280. });
  281. }
  282. function open() {
  283. calendar.value.open();
  284. }
  285. onReady(() => {});
  286. onShow(() => {
  287. //设置导航栏颜色
  288. uni.setNavigationBarColor({
  289. frontColor: "#000000", //字体颜色
  290. backgroundColor: "#ffffff", //背景颜色
  291. });
  292. //调用系统主题颜色
  293. // proxy.$settingStore.systemThemeColor([1]);
  294. });
  295. onLoad((options) => {
  296. if ("deviceId" in options) {
  297. deviceId.value = options.deviceId;
  298. }
  299. if ("productId" in options) {
  300. productId.value = parseInt(options.productId);
  301. init();
  302. }
  303. });
  304. watch(
  305. () => state.tabs.value,
  306. (val) => {
  307. if (val == 2) {
  308. deviceControlData();
  309. }
  310. }
  311. );
  312. </script>
  313. <style lang="scss" scoped>
  314. .header-area {
  315. border-radius: 10px;
  316. background: linear-gradient(to bottom, #fafbff, #e7f3ff);
  317. &-top {
  318. justify-content: space-between;
  319. .title {
  320. font-size: 18px;
  321. color: #000;
  322. font-weight: 600;
  323. }
  324. .status {
  325. font-size: 15px;
  326. color: #ffffff;
  327. padding: 2px 10px;
  328. border-radius: 20px;
  329. line-height: 22px;
  330. }
  331. }
  332. &-center {
  333. width: 100%;
  334. text-align: left;
  335. font-size: 14px;
  336. color: rgba(0, 0, 0, 0.7);
  337. &-item {
  338. > span {
  339. display: inline-block;
  340. line-height: 25px;
  341. }
  342. .title,
  343. .value {
  344. padding: 0px 5px 0px 5px;
  345. }
  346. }
  347. }
  348. }
  349. .body-area {
  350. .realTimeData-area {
  351. flex-wrap: wrap;
  352. line-height: 30px;
  353. font-size: 16px;
  354. &-item {
  355. width: calc(50% - 7.5px);
  356. padding: 10px;
  357. margin-bottom: 10px;
  358. border-radius: 5px;
  359. box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.1);
  360. background-color: #ffffff;
  361. &:nth-child(2n -1) {
  362. margin-right: 15px;
  363. }
  364. .title {
  365. font-size: 14px;
  366. }
  367. .value {
  368. display: inline-block;
  369. color: #000;
  370. font-size: 16px;
  371. font-weight: 600;
  372. }
  373. }
  374. }
  375. .cotrol-area {
  376. &-item {
  377. border-radius: 5px;
  378. box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.1);
  379. .title {
  380. color: #000;
  381. font-weight: 600;
  382. font-size: 14px;
  383. }
  384. }
  385. }
  386. .subsection {
  387. display: flex;
  388. justify-content: center;
  389. margin: 15px 0;
  390. &-item {
  391. margin-right: 15px;
  392. &:last-child {
  393. margin-right: 0;
  394. }
  395. }
  396. .active {
  397. font-weight: 600;
  398. animation: colorChange 1s forwards;
  399. @keyframes colorChange {
  400. 0% {
  401. color: #666666;
  402. }
  403. 100% {
  404. color: #000;
  405. } /* 深色 */
  406. }
  407. }
  408. }
  409. }
  410. :deep() {
  411. .u-slider__show-value {
  412. margin: 10px 0px 10px 18px !important;
  413. }
  414. }
  415. uni-page-body {
  416. background-color: #fff;
  417. }
  418. .demo-layout {
  419. padding: 15px 10px;
  420. box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.1);
  421. border-radius: 5px;
  422. }
  423. </style>