IotDeviceMainAlarm.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. <template>
  2. <el-row :gutter="20">
  3. <!-- 左侧部门树 -->
  4. <el-col :span="4" :xs="24">
  5. <ContentWrap class="h-1/1" v-if="treeShow">
  6. <DeptTree @node-click="handleDeptNodeClick" />
  7. </ContentWrap>
  8. </el-col>
  9. <el-col :span="contentSpan" :xs="24">
  10. <ContentWrap>
  11. <!-- 搜索工作栏 -->
  12. <el-form
  13. class="-mb-15px"
  14. :model="queryParams"
  15. ref="queryFormRef"
  16. :inline="true"
  17. label-width="68px"
  18. >
  19. <el-form-item :label="t('iotDevice.code')" prop="deviceCode" style="margin-left: 25px">
  20. <el-input
  21. v-model="queryParams.deviceCode"
  22. :placeholder="t('iotDevice.codeHolder')"
  23. clearable
  24. @keyup.enter="handleQuery"
  25. class="!w-200px"
  26. />
  27. </el-form-item>
  28. <el-form-item :label="t('iotDevice.name')" prop="deviceName">
  29. <el-input
  30. v-model="queryParams.deviceName"
  31. :placeholder="t('iotDevice.nameHolder')"
  32. clearable
  33. @keyup.enter="handleQuery"
  34. class="!w-200px"
  35. />
  36. </el-form-item>
  37. <el-form-item>
  38. <el-button @click="handleQuery"
  39. ><Icon icon="ep:search" class="mr-5px" />
  40. {{ t('file.search') }}</el-button
  41. >
  42. <el-button @click="resetQuery"
  43. ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('file.reset') }}</el-button
  44. >
  45. <el-button
  46. type="success"
  47. plain
  48. @click="handleExport"
  49. :loading="exportLoading"
  50. v-hasPermi="['rq:iot-device:export']"
  51. >
  52. <Icon icon="ep:download" class="mr-5px" /> 导出
  53. </el-button>
  54. </el-form-item>
  55. </el-form>
  56. </ContentWrap>
  57. <!-- 列表 -->
  58. <ContentWrap>
  59. <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  60. <el-table-column :label="t('monitor.serial')" width="70" align="center">
  61. <template #default="scope">
  62. {{ scope.$index + 1 }}
  63. </template>
  64. </el-table-column>
  65. <el-table-column :label="t('iotDevice.code')" align="center" prop="deviceCode" />
  66. <el-table-column :label="t('iotDevice.name')" align="center" prop="deviceName">
  67. <template #default="scope">
  68. <el-link :underline="false" type="primary" @click="handleDetail(scope.row.id)">
  69. {{ scope.row.deviceName }}
  70. </el-link>
  71. </template>
  72. </el-table-column>
  73. <el-table-column :label="t('bomList.serviceDue')" align="center">
  74. <template #default="scope">
  75. <template v-if="hasMaintenancePlan(scope.row.mainDistance)">
  76. <span :class="getDistanceClass(scope.row.mainDistance)">
  77. {{ scope.row.mainDistance }}
  78. </span>
  79. </template>
  80. <span v-else>无保养计划</span>
  81. </template>
  82. </el-table-column>
  83. <el-table-column :label="t('iotDevice.dept')" align="center" prop="deptName" />
  84. <el-table-column :label="t('monitor.status')" align="center" prop="deviceStatus">
  85. <template #default="scope">
  86. <dict-tag :type="DICT_TYPE.PMS_DEVICE_STATUS" :value="scope.row.deviceStatus" />
  87. </template>
  88. </el-table-column>
  89. <el-table-column :label="t('monitor.operation')" align="center" min-width="120px">
  90. <template #default="scope">
  91. <el-button
  92. link
  93. type="primary"
  94. @click="openBomForm(scope.row)"
  95. v-hasPermi="['rq:iot-device:query']"
  96. v-if="hasMaintenancePlan(scope.row.mainDistance)"
  97. >
  98. {{ t('monitor.details') }}
  99. </el-button>
  100. </template>
  101. </el-table-column>
  102. </el-table>
  103. <!-- 分页 -->
  104. <Pagination
  105. :total="total"
  106. v-model:page="queryParams.pageNo"
  107. v-model:limit="queryParams.pageSize"
  108. @pagination="getList"
  109. />
  110. </ContentWrap>
  111. </el-col>
  112. </el-row>
  113. <DeviceAlarmBomList ref="modelFormRef" :flag = "flag" />
  114. </template>
  115. <script setup lang="ts">
  116. import download from '@/utils/download'
  117. import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
  118. import { IotMainWorkOrderApi, IotMainWorkOrderVO } from '@/api/pms/iotmainworkorder'
  119. import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
  120. import DeptTree from '@/views/system/user/DeptTree.vue'
  121. import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
  122. import DeviceAlarmBomList from "@/views/pms/iotmainworkorder/DeviceAlarmBomList.vue";
  123. /** 设备台账 列表 */
  124. defineOptions({ name: 'IotDeviceMainAlarm' })
  125. const message = useMessage() // 消息弹窗
  126. const { t } = useI18n() // 国际化
  127. const { push } = useRouter() // 路由跳转
  128. const modelFormRef = ref()
  129. const loading = ref(true) // 列表的加载中
  130. const ifShow = ref(false)
  131. const list = ref<IotDeviceVO[]>([]) // 列表的数据
  132. const total = ref(0) // 列表的总页数
  133. const queryParams = reactive({
  134. pageNo: 1,
  135. pageSize: 10,
  136. deviceCode: undefined,
  137. deviceName: undefined,
  138. brand: undefined,
  139. model: undefined,
  140. deptId: undefined,
  141. deviceStatus: undefined,
  142. assetProperty: undefined,
  143. picUrl: undefined,
  144. remark: undefined,
  145. manufacturerId: undefined,
  146. supplierId: undefined,
  147. manDate: [],
  148. nameplate: undefined,
  149. expires: undefined,
  150. plPrice: undefined,
  151. plDate: [],
  152. plYear: undefined,
  153. plStartDate: [],
  154. plMonthed: undefined,
  155. plAmounted: undefined,
  156. remainAmount: undefined,
  157. infoId: undefined,
  158. infoType: undefined,
  159. infoName: undefined,
  160. infoRemark: undefined,
  161. infoUrl: undefined,
  162. templateJson: undefined,
  163. creator: undefined,
  164. setFlag: ''
  165. })
  166. const queryFormRef = ref() // 搜索的表单
  167. const flag = ref() // 查询保养计划或保养工单的标识
  168. const exportLoading = ref(false) // 导出的加载中
  169. const contentSpan = ref(20)
  170. const treeShow = ref(true)
  171. /** 查询列表 */
  172. const getList = async () => {
  173. loading.value = true
  174. try {
  175. const data = await IotMainWorkOrderApi.deviceMainDistances(queryParams)
  176. list.value = data.list
  177. total.value = data.total
  178. } finally {
  179. loading.value = false
  180. }
  181. }
  182. /** 处理部门被点击 */
  183. const handleDeptNodeClick = async (row) => {
  184. queryParams.deptId = row.id
  185. await getList()
  186. }
  187. /** 搜索按钮操作 */
  188. const handleQuery = () => {
  189. queryParams.pageNo = 1
  190. getList()
  191. }
  192. /** 重置按钮操作 */
  193. const resetQuery = () => {
  194. queryFormRef.value.resetFields()
  195. handleQuery()
  196. }
  197. const getDistanceClass = (distance: number | string | null) => {
  198. if (distance === null || distance === undefined) return '';
  199. // 如果是数字类型,直接处理
  200. if (typeof distance === 'number') {
  201. return distance < 0 ? 'negative-distance' :
  202. distance > 0 ? 'positive-distance' : '';
  203. }
  204. // 如果是字符串,提取数字部分
  205. if (typeof distance === 'string') {
  206. // 使用正则提取数字部分(包括负号、小数点和科学计数法)
  207. const numericPart = distance.match(/[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?/)?.[0];
  208. // 如果提取到数字部分,转换为数值
  209. if (numericPart) {
  210. const num = parseFloat(numericPart);
  211. return num < 0 ? 'negative-distance' :
  212. num > 0 ? 'positive-distance' : '';
  213. }
  214. }
  215. return '';
  216. };
  217. // 判断是否有保养计划
  218. const hasMaintenancePlan = (mainDistance: any) => {
  219. // 检查:非null、非undefined、非空字符串
  220. return mainDistance != null && mainDistance !== '';
  221. };
  222. const handleDetail = (id: number) => {
  223. push({ name: 'DeviceDetailInfo', params: { id } })
  224. }
  225. const drawerVisible = ref<boolean>(false)
  226. const showDrawer = ref()
  227. const openBomForm = async (row) => {
  228. if (row.workOrderId) {
  229. flag.value = 'workOrder';
  230. modelFormRef.value.open(row.workOrderId, flag.value, row.id)
  231. } else if (row.planId) {
  232. flag.value = 'plan';
  233. modelFormRef.value.open(row.planId, flag.value, row.id)
  234. }
  235. }
  236. /** 导出按钮操作 */
  237. const handleExport = async () => {
  238. try {
  239. // 导出的二次确认
  240. await message.exportConfirm()
  241. // 发起导出
  242. exportLoading.value = true
  243. const data = await IotDeviceApi.exportIotDevice(queryParams)
  244. download.excel(data, '设备台账.xls')
  245. } catch {
  246. } finally {
  247. exportLoading.value = false
  248. }
  249. }
  250. const { wsCache } = useCache()
  251. /** 初始化 **/
  252. onMounted(() => {
  253. getList()
  254. })
  255. </script>
  256. <style scoped>
  257. /* 正数样式 - 淡绿色 */
  258. .positive-distance {
  259. color: #67c23a; /* element-plus 成功色 */
  260. background-color: rgba(103, 194, 58, 0.1); /* 10% 透明度的淡绿色背景 */
  261. padding: 2px 8px;
  262. border-radius: 4px;
  263. display: inline-block;
  264. }
  265. /* 负数样式 - 淡红色 */
  266. .negative-distance {
  267. color: #f56c6c; /* element-plus 危险色 */
  268. background-color: rgba(245, 108, 108, 0.1); /* 10% 透明度的淡红色背景 */
  269. padding: 2px 8px;
  270. border-radius: 4px;
  271. display: inline-block;
  272. }
  273. </style>